npm package: @salesforce/metadata-visualizer-core
Description: Platform agnostic framework for visualizing Salesforce metadata with extensible plugin system
Author: salesforce
Latest version: 1.0.1

# @salesforce/metadata-visualizer-core

Platform-agnostic framework for visualizing Salesforce metadata with an extensible plugin system.

## Overview

The Salesforce Metadata Visualizer Framework is a **platform-agnostic** TypeScript library for building metadata visualization tools. It provides a plugin-based architecture that works across VS Code, CLI tools, web applications, and other environments.

### Features

- **Platform-Agnostic** - Works in VS Code, Node.js, browsers, and more
- **Plugin-Based Architecture** - Extensible system for custom visualizers
- **Modular Design** - Clean separation of concerns (parse → render → display)
- **Type-Safe** - Full TypeScript support with comprehensive type definitions
- **Lightweight** - Minimal dependencies, focused API
- **Contract Enforcement** - Framework validates plugin metadata and structure

## Installation

```bash
npm install @salesforce/metadata-visualizer-core
```

## Quick Start

```typescript
import {
  PluginManager,
  VisualizationEngine,
} from '@salesforce/metadata-visualizer-core';
import { IPlatformContext } from '@salesforce/metadata-core-sdk';

// Initialize framework with platform context
const context: IPlatformContext = /* your platform context */;
const pluginManager = new PluginManager(context);
const engine = new VisualizationEngine(context, pluginManager);

// Load plugins (automatically loads from @salesforce/metadata-plugins via getAllVisualizers())
await pluginManager.loadPlugins();

// Visualize a metadata file
const result = await engine.visualize('/path/to/Account.object-meta.xml');
if (result) {
  console.log(result.rendered.html);
}
```

## Architecture

The framework follows a layered architecture:

```
┌─────────────────────────────────────────┐
│   Platform Layer                        │
│   - VS Code, Node.js, Browser          │
│   - IPlatformContext                    │
│     (IFileSystem, ILogger, ITelemetry)  │
└──────────────────┬──────────────────────┘
                   │
┌──────────────────▼──────────────────────┐
│   Core Framework                        │
│   - VisualizationEngine                 │
│   - PluginManager                       │
│   - HtmlVizGenerator                    │
│   - ErrorManager                        │
└──────────────────┬──────────────────────┘
                   │
┌──────────────────▼──────────────────────┐
│   Plugin System (SDK)                   │
│   - IVisualizationPlugin                │
│   - IMetadataParser                     │
│   - MetadataPluginInfo                  │
└─────────────────────────────────────────┘
```

### Data Flow

```
1. Input: Metadata File
   ↓
2. Plugin Discovery: Find plugin via canHandle()
   ↓
3. Version Info: Collect project version info
   ↓
4. Parser: Extract structured data via parser.parse(filePath, versionInfo)
   ↓
5. Validation: Optional validate(data) check
   ↓
6. Rendering: Auto-detect React build in ui/ directory, generate HTML via HtmlVizGenerator
   ↓
7. Display: Platform renders HTML (webview, browser, etc.)
```

## Core API

### VisualizationEngine

Main entry point for visualizing metadata.

```typescript
class VisualizationEngine {
  constructor(context: IPlatformContext, pluginManager: PluginManager);

  /**
   * Visualize and render a metadata file.
   * Returns null on errors (errors reported to ErrorManager).
   */
  async visualize<T = any>(filePath: string): Promise<VisualizationResult<T> | null>;

  /** Check if a file can be visualized by any registered plugin */
  canVisualize(filePath: string): boolean;

  /** Get all supported file patterns across registered plugins */
  getSupportedFilePatterns(): string[];

  /** Get the ErrorManager instance for subscribing to errors */
  getErrorManager(): ErrorManager;

  /** Dispose resources */
  dispose(): void;
}
```

### PluginManager

Manages plugin registration and lifecycle.

```typescript
class PluginManager {
  constructor(context: IPlatformContext);

  /**
   * Load plugins from plugin package.
   * Automatically loads via getAllVisualizers() from @salesforce/metadata-plugins.
   *
   * Framework responsibilities:
   * - Calls getMetadataPluginInfo() hook on each plugin
   * - Validates plugin metadata (id, name, author, filePatterns)
   * - Validates plugin structure (parser, canHandle)
   * - Initializes plugins with platform context
   * - Registers plugins in internal registry
   *
   * @returns Number of successfully loaded plugins
   */
  async loadPlugins(): Promise<number>;

  /** Find the best plugin that can handle the given file (highest priority) */
  findVisualizerPluginForFile(filePath: string): IVisualizationPlugin | undefined;

  /** Get all registered visualizer plugins */
  getAllVisualizerPlugins(): IVisualizationPlugin[];

  /** Register a visualizer plugin manually */
  async registerVisualizer(plugin: IVisualizationPlugin): Promise<void>;

  /** Unregister a visualizer plugin by ID */
  unregisterVisualizer(pluginId: string): void;

  /** Check if any plugin can handle the file */
  isSupportedFileForVisualization(filePath: string): boolean;

  /** Get all supported file patterns */
  getSupportedFilePatterns(): string[];

  /** Reload all plugins */
  async reloadAll(): Promise<void>;
}
```

### HtmlVizGenerator

Generates HTML from React build output for plugin rendering.

```typescript
class HtmlVizGenerator {
  constructor(fileSystem: IFileSystem);

  /** Generate complete HTML document from React build + plugin data */
  async generate(config: HtmlVizConfig): Promise<string>;
}

interface HtmlVizConfig {
  buildDir: string; // Path to React build output directory
  pluginId: string; // Plugin identifier
  data: any; // Plugin data to inject into React app
  filePath: string; // File path being visualized
}
```

### ErrorManager

Central error management service.

```typescript
class ErrorManager {
  /** Report a new error (overwrites previous, notifies callback) */
  reportError(error: PluginError): void;

  /** Clear the current error */
  clearError(): void;

  /** Retrieve the current error */
  getError(): PluginError | null;

  /** Set callback invoked when an error is reported */
  setOnErrorReported(callback: ((error: PluginError) => void) | undefined): void;

  /** Dispose and clean up */
  dispose(): void;
}
```

### Platform Context

Provided by core-sdk (`@salesforce/metadata-core-sdk`). See the `@salesforce/metadata-core-sdk` package for details.

```typescript
interface IPlatformContext {
  readonly fileSystem: IFileSystem;
  readonly logger: ILogger;
  readonly telemetry: ITelemetry;
  readonly reactBuildsBasePath: string;
  readonly pluginSettings?: Record<string, unknown>;
}
```

## Building Plugins

### Plugin Contract

Every plugin implements `IVisualizationPlugin` from `@salesforce/metadata-core-sdk`:

```typescript
interface IVisualizationPlugin<TData = any> {
  /** Get plugin metadata (mandatory hook, called during registration) */
  getMetadataPluginInfo(): MetadataPluginInfo;

  /** Parser for extracting structured data (required) */
  parser: IMetadataParser<TData>;

  /** Check if this plugin can handle the given file (required) */
  canHandle(filePath: string): boolean;

  /** Initialize plugin with platform context (required) */
  initialize(context: IPlatformContext): Promise<void>;

  /** Dispose plugin resources (optional) */
  dispose?(): void;
}
```

**Note**: Rendering is handled automatically by the framework. The framework auto-detects React builds by checking for `ui/` directory in plugin build outputs.

### Metadata Contract

Plugins must provide metadata via `getMetadataPluginInfo()`:

```typescript
interface MetadataPluginInfo {
  id: string; // Must match plugin folder name
  name: string; // Human-readable name
  description?: string; // Plugin description (optional)
  author: string; // Plugin author/team
  filePatterns: string[]; // File patterns (e.g., ['.object-meta.xml'])
  priority?: number; // Selection priority when multiple plugins match (default: 0)
  trustedSources?: {
    // CSP directives for external resources (optional)
    scripts?: string[];
    styles?: string[];
    connect?: string[];
  };
}
```

### Example: Custom Object Visualizer

```typescript
import {
  IVisualizationPlugin,
  MetadataPluginInfo,
  IMetadataParser,
  IPlatformContext,
} from '@salesforce/metadata-core-sdk';
import type { VersionInfo } from '@salesforce/metadata-core-sdk';

interface ObjectMetadata {
  name: string;
  fields: Field[];
}

class ObjectParser implements IMetadataParser<ObjectMetadata> {
  async parse(filePath: string, versionInfo: VersionInfo): Promise<ObjectMetadata> {
    // Parse XML and return structured data
    return { name: 'Account', fields: [] };
  }
}

export class ObjectVisualizer implements IVisualizationPlugin<ObjectMetadata> {
  private readonly pluginMetadata: MetadataPluginInfo = {
    id: 'object', // Must match folder name
    name: 'Object Visualizer',
    description: 'Visualize Salesforce Custom Objects',
    author: 'Object Team',
    filePatterns: ['.object-meta.xml'],
  };

  readonly parser: IMetadataParser<ObjectMetadata>;

  constructor() {
    this.parser = new ObjectParser();
  }

  getMetadataPluginInfo(): MetadataPluginInfo {
    return this.pluginMetadata;
  }

  canHandle(filePath: string): boolean {
    return filePath.endsWith('.object-meta.xml');
  }

  async initialize(context: IPlatformContext): Promise<void> {
    // Initialize plugin with platform context
  }

  // Framework auto-detects React builds and handles rendering automatically
}
```

### Plugin Lifecycle

1. **Registration** - Framework calls `getMetadataPluginInfo()` and validates metadata
2. **Initialization** - Framework calls `initialize(context)` once
3. **File Handling** - Framework calls `canHandle(filePath)` to find matching plugin
4. **Parsing** - Framework calls `parser.parse(filePath, versionInfo)` to extract data
5. **Validation** - Framework calls `parser.validate(data)` if provided
6. **Rendering** - Framework auto-detects React builds in `ui/` directory and generates HTML
7. **Disposal** - Framework calls `dispose()` when plugin is unloaded

## Plugin Metadata Validation

The framework enforces plugin metadata contract:

- Validates `id` is non-empty string (must match folder name)
- Validates `name` is non-empty string
- Validates `author` is non-empty string
- Validates `filePatterns` is non-empty array with non-empty strings
- TypeScript enforces plugin structure (`parser`, `canHandle`, etc.)

## Package Exports

```typescript
// Core classes
import {
  PluginManager,
  VisualizationEngine,
  HtmlVizGenerator,
  ErrorManager,
} from '@salesforce/metadata-visualizer-core';

// Types
import type {
  VisualizationResult,
  RenderedContent,
  HtmlVizConfig,
  ErrorListener,
} from '@salesforce/metadata-visualizer-core';
```

## License

BSD-3-Clause

## Support

For issues or questions, please file an issue at:
https://github.com/forcedotcom/salesforce-metadata-visualizer-support/issues