A comprehensive development toolkit for Microsoft Dataverse, featuring an extensible plugin architecture and modern cross-platform UI.
- Cross-Platform Desktop - Native .NET MAUI application for Windows and macOS
- Modern Web UI - React 18 + TypeScript with FluentUI v9 design system
- Extensible Plugins - Out-of-process plugin architecture with sandboxed execution
- Connection Management - Manage multiple Dataverse environments with OAuth authentication
- Tab Workspace - Run multiple plugin instances simultaneously with drag-and-drop tabs
- Solution Analysis - Deep analysis of solution component layering across environments
- Module Federation - Dynamic plugin loading at runtime
- Dark Mode - Full theme support (light/dark/system)
DataverseDevKit includes a powerful plugin system that allows extending functionality. Plugins run in isolated processes with their own UI components loaded dynamically.
Purpose: Perform in-depth analysis of solution components and their layering across Dataverse environments.
Key Features:
- Solution Discovery - Search and filter solutions by name, publisher, version, and managed status
- Layer Stack Analysis - Build complete layer stacks for components across multiple solutions
- Advanced Filtering - Query components with complex rules (HAS, ORDER_STRICT, ORDER_FLEX)
- Diff Views - Compare component payloads (XML/JSON) between different layers
- High Performance - In-memory SQLite database with parallel queries and indexing
Supported Component Types:
- Forms (systemform)
- Views (savedquery)
- Entities (entity)
- Attributes (attribute)
- Ribbons (ribboncustomization)
- Web Resources (webresource)
- Plugin Steps (sdkmessageprocessingstep)
- App Modules (appmodule)
Commands:
index- Build an index of solutions, components, and layersquery- Query indexed components with advanced filteringdetails- Get full layer stack for a specific componentdiff- Compare payloads between two layers
Status: Fully implemented with comprehensive React UI including dashboards, visualizations (tree, Sankey, heatmap, network graph, etc.), filtering, and diff views.
DataverseDevKit uses a layered architecture with clear separation of concerns and secure inter-process communication.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β User Interface β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Shell UI (React + TypeScript) β β
β β β’ Connection Management β’ Plugin Marketplace β β
β β β’ Tab Workspace β’ Settings & Themes β β
β β β β
β β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β β
β β β Plugin UI 1 β β Plugin UI 2 β β Plugin UI n β β β
β β β (Module Fed) β β (Module Fed) β β (Module Fed) β β β
β β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
JSON-RPC over HybridWebView
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β MAUI Host (.NET) β
β β
β ββββββββββββββββββββ βββββββββββββββββββββββββββββββββββ β
β β JSON-RPC Bridge β β Core Services β β
β β β’ Method Router β β β’ PluginHostManager β β
β β β’ Event Emitter β β β’ ConnectionService β β
β ββββββββββββββββββββ β β’ TokenCallbackServer β β
β βββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
gRPC over Unix Domain Socket
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Plugin Runtime Worker (.NET) β
β β
β ββββββββββββββββββββ βββββββββββββββββββββββββββββββββββ β
β β gRPC Server β β Plugin Loader β β
β β β’ PluginHost β β β’ Assembly Isolation β β
β β β’ TokenProvider β β β’ Dependency Management β β
β ββββββββββββββββββββ β β’ Plugin Load Context β β
β βββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
Plugin Interface (IToolPlugin)
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Plugin DLLs β
β β
β ββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββ β
β β Solution Layer β β Custom Plugins β β
β β Analyzer β β β’ Community β β
β β β’ In-memory SQLite β β β’ Third-party β β
β β β’ EF Core β β β’ Organization-specific β β
β ββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
Microsoft Dataverse APIs
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Microsoft Dataverse β
β (Power Platform Environment) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Technology: React 18, TypeScript, FluentUI v9, Vite, Module Federation
Responsibilities:
- Render application shell (tabs, sidebar, marketplace, settings)
- Load plugin UI components dynamically via Module Federation
- Manage UI state with Zustand
- Handle user interactions and route commands to backend
Communication: Uses JSON-RPC protocol over MAUI's HybridWebView to communicate with the Host layer. Messages are serialized as JSON and transmitted bidirectionally.
Technology: .NET 10, .NET MAUI, HybridWebView
Responsibilities:
- Provide native desktop window and WebView host
- Implement JSON-RPC bridge for UI β Host communication
- Manage plugin lifecycle (start, stop, health checks)
- Handle OAuth token management and storage
- Route plugin commands to appropriate Plugin Runtime workers
- Emit events back to UI (progress updates, notifications)
Key Components:
JsonRpcBridge.cs- Dispatcher for method calls from UIPluginHostManager.cs- Manages plugin worker processesConnectionService.cs- Stores and retrieves connection credentialsTokenCallbackServer.cs- OAuth callback listener
Communication:
- Upstream (to UI): JSON-RPC responses and events via HybridWebView
- Downstream (to Plugins): gRPC calls over Unix domain sockets
Technology: .NET 10, gRPC, Protocol Buffers
Responsibilities:
- Run as separate process for each plugin instance (sandboxing)
- Load plugin assemblies with isolated AssemblyLoadContext
- Expose gRPC server for Host to invoke plugin commands
- Call back to Host for Dataverse authentication tokens
- Manage plugin dependencies without affecting Host
Key Components:
Program.cs- gRPC server startupPluginLoader.cs- Dynamic assembly loadingPluginHostGrpcService.cs- Implements gRPC service contract
Communication:
- Upstream (to Host): gRPC responses with command results
- Downstream (to Plugins): Direct method invocation on
IToolPlugininterface
Technology: .NET 10, Custom plugin logic
Responsibilities:
- Implement
IToolPlugininterface - Execute plugin-specific business logic
- Interact with Dataverse APIs using provided tokens
- Return structured data to Runtime layer
Interface:
public interface IToolPlugin
{
string PluginId { get; }
string Name { get; }
Task InitializeAsync(IPluginContext context, CancellationToken ct);
Task<object?> ExecuteAsync(string command, string? payload, CancellationToken ct);
Task ShutdownAsync(CancellationToken ct);
}Communication: Synchronous method calls from Plugin Runtime layer
Used for bidirectional communication between React UI and .NET Host. The UI can send commands to the host, and the host can push events to the UI.
Request Format (UI β Host):
{
"jsonrpc": "2.0",
"id": "unique-request-id",
"method": "plugin.execute",
"params": {
"pluginId": "com.ddk.solutionlayeranalyzer",
"instanceId": "instance-123",
"command": "index",
"payload": "{...}"
}
}Response Format (Host β UI):
{
"jsonrpc": "2.0",
"id": "unique-request-id",
"result": {
"success": true,
"data": {...}
}
}Events (Host β UI, Push Notifications):
{
"jsonrpc": "2.0",
"method": "plugin.event",
"params": {
"event": "plugin:sla:progress",
"data": {"phase": "layers", "percent": 45}
}
}Event Subscription (UI):
Plugins subscribe to events using the singleton hostBridge:
import { hostBridge } from '@ddk/host-sdk';
// Subscribe to events from plugin backend
hostBridge.addEventListener('plugin:sla:progress', (data) => {
console.log('Progress:', data.phase, data.percent);
});
hostBridge.addEventListener('plugin:sla:index-complete', (data) => {
console.log('Indexing complete:', data.stats);
});Defined in src/dotnet/Contracts/pluginhost.proto:
Services:
PluginHostService- Host calls plugin commandsTokenProviderHostService- Plugin requests auth tokens
Key RPCs:
service PluginHostService {
rpc Initialize(InitializeRequest) returns (InitializeResponse);
rpc Execute(ExecuteRequest) returns (ExecuteResponse);
rpc Shutdown(ShutdownRequest) returns (ShutdownResponse);
}
service TokenProviderHostService {
rpc GetToken(GetTokenRequest) returns (GetTokenResponse);
}Plugin UI components are loaded dynamically at runtime using Vite Module Federation. The shell loads plugin remotes dynamically based on plugin manifests, rather than having them hardcoded in configuration.
Shell Configuration (web/apps/shell/vite.config.ts):
federation({
name: 'shell',
remotes: {}, // Empty - plugins loaded dynamically at runtime
shared: {
'react': { version: '18.3.1' },
'react-dom': { version: '18.3.1' },
'@fluentui/react-components': {},
'@fluentui/react-icons': {},
'@ddk/host-sdk': {}, // Includes singleton hostBridge instance
}
})Plugin Configuration (example vite.config.ts):
federation({
name: 'solutionLayerAnalyzer',
filename: 'remoteEntry.js',
exposes: {
'./Plugin': './src/Plugin.tsx'
},
shared: ['react', 'react-dom', '@fluentui/react-components', '@ddk/host-sdk']
})HostBridge Singleton: The @ddk/host-sdk package exports a singleton hostBridge instance that all plugins share for communication with the host. This ensures consistent JSON-RPC messaging and event handling across all plugin instances.
// From @ddk/host-sdk
import { hostBridge } from '@ddk/host-sdk';
// All plugins use the same instance
const result = await hostBridge.executeCommand({ pluginId, command, payload });This architecture ensures:
- Shared dependencies are loaded once
- Plugins are isolated in their own bundles
- Dynamic loading without rebuilding the Shell
- Consistent communication layer via singleton hostBridge
- User Action: User clicks "Analyze Solutions" in the Solution Layer Analyzer UI
- UI β Host: Plugin UI calls
hostBridge.executeCommand(), which sends JSON-RPC request to Host - Host Processing:
JsonRpcBridgereceives request, routes toPluginHostManager - Host β Runtime:
PluginHostManagersends gRPCExecutecall to Plugin Runtime worker - Runtime β Plugin: Plugin Runtime invokes
plugin.ExecuteAsync("index", payload) - Plugin Execution:
- Plugin calls back to Host via gRPC to get Dataverse token
- Plugin queries Dataverse APIs
- Plugin processes data (e.g., builds SQLite index)
- Plugin emits progress events back through chain
- Response Chain: Result flows back: Plugin β Runtime β Host β UI
- UI Update: React component receives result and updates display
DataverseDevKit/
βββ src/
β βββ dotnet/ # .NET Backend
β β βββ Host/ # MAUI Desktop Application
β β β βββ Bridge/ # JSON-RPC implementation
β β β βββ Services/ # Core services
β β β βββ wwwroot/ # Embedded web assets
β β βββ PluginRuntime/ # Out-of-process plugin worker
β β β βββ Services/ # gRPC service implementation
β β β βββ Runtime/ # Plugin loader
β β βββ Shared/ # Core abstractions
β β β βββ Abstractions/ # Plugin interfaces
β β β βββ Models/ # Shared DTOs
β β βββ Contracts/ # gRPC Protocol Buffers
β β βββ DataverseDevKit.slnx # .NET Solution
β βββ plugins/ # Plugin Projects
β βββ solution-layer-analyzer/ # Solution Layer Analyzer
β βββ src/ # Backend implementation (.NET)
β βββ ui/ # Frontend UI (React, fully implemented)
β βββ plugin.manifest.json # Plugin metadata
βββ web/ # Web Frontend (pnpm monorepo)
β βββ packages/
β β βββ host-sdk/ # TypeScript SDK for plugins
β β βββ ui-components/ # Shared FluentUI components
β βββ apps/
β βββ shell/ # Main Shell application
βββ tools/ # Build tools and schemas
βββ docs/ # Additional documentation
βββ build-web.ps1 # Web build script
βββ README.md # This file
- .NET MAUI - Cross-platform desktop framework
- HybridWebView - Native WebView with JavaScript interop
- gRPC - High-performance inter-process communication
- Protocol Buffers - Efficient binary serialization
- Microsoft.PowerPlatform.Dataverse.Client - Official Dataverse SDK
- SQLite + EF Core - In-memory data processing (plugins)
- React 18 - UI library with concurrent features
- TypeScript 5.7 - Type-safe JavaScript
- FluentUI v9 - Microsoft's design system
- Vite 6 - Next-generation build tool
- pnpm - Fast, disk-efficient package manager
- Zustand - Lightweight state management
- @dnd-kit - Modern drag-and-drop library
- Module Federation - Dynamic remote module loading
# .NET 10 SDK
dotnet --version # Should be 10.0.0 or higher
# Node.js 20+ and pnpm
node --version # Should be >= 20
pnpm --version # Should be >= 9
# Install pnpm if needed
npm install -g pnpm@latest# .NET dependencies
cd src/dotnet
dotnet restore DataverseDevKit.slnx
# Web dependencies
cd ../../web
pnpm installRun the full application in development mode:
# Terminal 1: Build web assets and run MAUI app
./build-web.ps1
cd src/dotnet
dotnet run --project Host/DataverseDevKit.Host.csprojOr run UI standalone for faster development:
# Shell UI (hot reload enabled)
cd web/apps/shell
pnpm dev
# Opens at http://localhost:5173# Build web frontend
./build-web.ps1
# Build .NET solution
cd src/dotnet
dotnet build DataverseDevKit.slnx -c Release
# Publish MAUI app
dotnet publish Host/DataverseDevKit.Host.csproj -c ReleasePlugins consist of two parts: backend (.NET) and frontend (React). Both are optional depending on your needs.
- Create new plugin project:
mkdir src/plugins/my-plugin
cd src/plugins/my-plugin
dotnet new classlib -f net10.0- Add reference to Shared:
<ItemGroup>
<ProjectReference Include="../../dotnet/Shared/DataverseDevKit.Shared.csproj" />
</ItemGroup>- Implement
IToolPlugin:
using DataverseDevKit.Shared.Abstractions;
public class MyPlugin : IToolPlugin
{
public string PluginId => "com.mycompany.myplugin";
public string Name => "My Plugin";
public async Task InitializeAsync(IPluginContext context, CancellationToken ct)
{
// Setup resources
}
public async Task<object?> ExecuteAsync(string command, string? payload, CancellationToken ct)
{
return command switch
{
"myCommand" => await HandleMyCommand(payload, ct),
_ => throw new ArgumentException($"Unknown command: {command}")
};
}
public async Task ShutdownAsync(CancellationToken ct)
{
// Cleanup
}
}- Create
plugin.manifest.json:
{
"id": "com.mycompany.myplugin",
"name": "My Plugin",
"version": "1.0.0",
"description": "Does amazing things",
"backend": {
"assembly": "backend/MyPlugin.dll",
"entryPoint": "MyCompany.MyPlugin"
}
}- Create plugin UI project:
cd web/plugins/first-party
pnpm create vite my-plugin-ui --template react-ts
cd my-plugin-ui- Configure Module Federation in
vite.config.ts:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import federation from '@originjs/vite-plugin-federation'
export default defineConfig({
plugins: [
react(),
federation({
name: 'myPluginUi',
filename: 'remoteEntry.js',
exposes: {
'./Plugin': './src/Plugin.tsx'
},
shared: ['react', 'react-dom', '@fluentui/react-components']
})
],
build: {
target: 'esnext'
}
})- Create plugin component:
import { FC } from 'react';
import { Button } from '@fluentui/react-components';
import { useHostBridge } from '@ddk/host-sdk';
interface PluginProps {
instanceId: string;
connectionId?: string;
}
const MyPlugin: FC<PluginProps> = ({ instanceId, connectionId }) => {
const hostBridge = useHostBridge();
const handleAction = async () => {
const result = await hostBridge.executeCommand({
pluginId: 'com.mycompany.myplugin',
command: 'myCommand',
payload: JSON.stringify({ data: 'test' })
});
console.log('Result:', result);
};
return (
<div>
<h1>My Plugin</h1>
<Button onClick={handleAction}>Execute Command</Button>
</div>
);
};
export default MyPlugin;- Update manifest to include UI:
{
"ui": {
"devEntry": "http://localhost:5175/dist/assets/remoteEntry.js",
"entry": "frontend/assets/remoteEntry.js",
"module": "./Plugin",
"scope": "myPluginUi"
}
}For comprehensive plugin development guide, see web/README.md.
# .NET tests
cd src/dotnet
dotnet test
# Web type checking
cd web
pnpm type-check
# Lint
pnpm lint- Web Frontend Guide - Detailed frontend development documentation
- Plugin Development - Plugin structure and build instructions
- Solution Layer Analyzer - Plugin-specific documentation
cd web
rm -rf node_modules
rm pnpm-lock.yaml
pnpm install
pnpm buildcd src/dotnet
dotnet clean DataverseDevKit.slnx
dotnet restore DataverseDevKit.slnx
dotnet build DataverseDevKit.slnx- Check plugin manifest syntax
- Verify plugin DLL is built and in correct location
- Check MAUI Host logs for plugin startup errors
- Ensure plugin implements
IToolPlugininterface correctly
See LICENSE file for details.
Built with β€οΈ for the Dataverse community