npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@salesforce/metadata-visualizer-web

v1.1.0

Published

Web/HTTP SDK for the Salesforce Metadata Visualizer framework. Lets any HTTP server plug a host-supplied IFileSystem into the framework and serve plugin visualizations.

Readme

@salesforce/metadata-visualizer-web

Web/HTTP SDK for the Salesforce Metadata Visualizer framework (@salesforce/metadata-visualizer-core). Lets any HTTP-based host (custom web services, browser applications, demo pages) plug a host-supplied filesystem into the framework and serve plugin visualizations to a browser.

This package mirrors the role visualizer-vscode-ext plays for VS Code: it owns the host-specific plumbing (postMessage bootstrap, plugin asset bundling, @dist/ URL rewriting) and exposes a small façade so consumers never see plugin internals.

Interfaces the host must implement

The SDK is built around the principle of interface inversion: we define small contracts in core-sdk, and each host (browser application, VS Code, others) implements them. The SDK does not impose a transport, a router, or a filesystem layout — it just consumes what the host supplies and returns blobs the host can serve.

There is exactly one interface the host must implement: IFileSystem. The engine also accepts an optional telemetry hook; everything else is handled internally by the SDK.

Required: IFileSystem (from @salesforce/metadata-core-sdk)

This is the only interface the host must implement. It is the host's bridge between the framework and the user-project files the host owns.

Why this is required

Plugin parsers (SchemaParser, FlexipageParser, future plugins) read the user's metadata files through this interface. The host implements it once against its existing I/O layer — delegating to your project service's file operations, which should enforce path traversal guards, restricted-path rules (.git, .sf, node_modules, dotfiles), path length limits, and per-project isolation.

Methods to implement

| Method | What it does | Implementation notes | | -------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | | readFile(path) | Read a file as a UTF-8 string. Returns Result<string>. | Delegate to your file I/O layer. Wrap path validation errors into { success: false, error }. | | writeFile(path, content) | Write a file. Returns Result<void>. | Plugins do not write today; you can throw or stub. | | exists(path) | Whether a file or directory exists. Returns Promise<boolean>. | Use your project service's path resolution + file system checks. | | getMetadata(path) | Returns { path, fileName, extension }. | Pure path operations + stat for existence. | | readDirectory(path) | Lists directory entries with { name, isFile }. | Delegate to your directory listing implementation. | | findFiles(pattern, maxResults?) | Workspace-wide glob (e.g. **/*.object-meta.xml). | Pattern language is minimatch with POSIX separators, anchored at the workspace root. Schema plugin uses this to discover related objects. | | getWorkspaceRoot() | Absolute path of the project root. | One project per IFileSystem instance — construct one per request bound to that request's project. | | normalizePath, separator, basename, dirname, join, extname, parse, relative, resolve, isAbsolute | Pure string operations. | Delegate to Node's path module. |

The full type definition lives at @salesforce/metadata-core-sdkIFileSystem.

Optional: telemetry (ITelemetry from @salesforce/metadata-core-sdk)

A telemetry hook for tracking usage and errors, passed to createWebVisualizationEngine alongside fileSystem. If the host doesn't supply it, the SDK falls back to a no-op implementation.


What the host gets back from the SDK

After implementing IFileSystem and constructing the engine via createWebVisualizationEngine, the host calls these methods. The WebVisualizationEngine class provides five core methods:

Core API Methods

| Method | Returns | Use Case | | -------------------------------------------- | ------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | | getPlugin(metadataFilePath) | PluginInfo \| null | Resolve a metadata file path to its registered plugin (returns id, displayName, etc.) | | visualizeMetadata(pluginId) | { bundle: HttpAsset<string> \| null, error: PluginError \| null } | Generate a fully self-contained HTML shell for the plugin's UI (no parsing — UI only) | | getParsedMetadata(metadataFilePath) | VisualizeOutcome{ result, error } | Parse metadata and return structured data without generating HTML | | getUpdatedParsedMetadata(metadataFilePath) | { error, pluginDataUpdateMessage } | Generate update message for posting to existing plugin iframe (live updates) | | dispose() | void | Release all resources (call on shutdown) |

Method Details

Two-step flow. UI generation and metadata parsing are separate calls. visualizeMetadata produces the static plugin UI shell (no file I/O, no parsing); the loaded iframe then requests parsed data via postMessage, which the host serves from getParsedMetadata. Use getPlugin(filePath) to resolve a metadata file to its plugin ID before calling visualizeMetadata.

getPlugin(metadataFilePath) — Resolve plugin for a file:

  • Matches the file path against registered plugin file patterns
  • Returns PluginInfo (with id) on match, null otherwise
  • Synchronous and side-effect-free

visualizeMetadata(pluginId) — Generate UI shell:

  • Returns fully self-contained HTML with all assets (JS, CSS, platform styles) inlined
  • No metadata parsing occurs — this generates the static plugin UI only
  • Zero additional HTTP requests needed
  • Perfect for iframe embedding: <iframe srcdoc="..."></iframe>
  • The loaded plugin iframe requests its data separately via postMessage
  • Error handling: Check error field first, then use bundle

getParsedMetadata(metadataFilePath) — Parse metadata only:

  • Returns parsed metadata as structured data
  • Use for REST endpoints, data analysis, validation, and as the data source for plugin postMessage requests
  • No HTML generation overhead

getUpdatedParsedMetadata(metadataFilePath) — Live updates:

  • Parses file and generates PluginDataUpdateMessage ready for iframe postMessage
  • Use with file watchers to push updates to already-loaded visualizations
  • Plugins listen via onDataUpdate from HostCommunicationUtils

Integration walkthrough

This section explains how a web host integrates the SDK end-to-end. The actual route plumbing depends on your framework conventions (Express, Fastify, etc.). The shape below shows the integration pattern at the seams.

Step 1 — Implement IFileSystem

The host writes a single class that wraps its existing file I/O layer. Each method delegates to your existing file operations. The class is constructed per request, bound to that request's project root.

// Host implementation — sketch only
class MyHostFileSystem implements IFileSystem {
  constructor(private readonly projectDir: string) {}
  // readFile, findFiles, exists, getMetadata, readDirectory →
  //     delegate to your file service
  // pure-string helpers (basename, join, etc.) → delegate to node:path
  // getWorkspaceRoot() → return this.projectDir
}

Step 2 — Construct the engine per request

For each incoming HTTP request that needs visualization:

const fileSystem = new MyHostFileSystem(projectDir);
const engine = await createWebVisualizationEngine({ fileSystem });
// Use the engine, then dispose when the request ends.

Per-request construction is acceptable for prototypes because plugin loading is cheap. Production hosts can cache engines per project if profiling shows it matters.

Step 3 — Wire HTTP routes

The host adds visualizer routes to its service using the five core WebVisualizationEngine methods:

Route 1: Generate HTML Bundle

app.post('/visualizer/bundle', async (req, res) => {
  const { filePath } = req.body;

  // Resolve file → plugin first; visualizeMetadata takes a pluginId, not a path.
  const pluginInfo = engine.getPlugin(filePath);
  if (!pluginInfo) {
    return res.status(415).json({ error: 'No plugin found for this file type' });
  }

  const { bundle, error } = await engine.visualizeMetadata(pluginInfo.id);

  if (error) {
    const status = error.category === 'Configuration' ? 415 : 500;
    return res.status(status).json({
      error: error.message,
      category: error.category,
      context: error.context,
    });
  }

  res.type(bundle.contentType).set('Cache-Control', bundle.cacheControl).send(bundle.body);
});

Use case: Generate the plugin UI shell for iframe embedding. Returns fully self-contained HTML with all assets inlined. Note: this generates the UI only — the loaded iframe requests parsed metadata separately via postMessage, served by the parse route below.

Route 2: Parse Metadata

app.post('/visualizer/parse', async (req, res) => {
  const { filePath } = req.body;
  const outcome = await engine.getParsedMetadata(filePath);

  if (outcome.error) {
    return res.status(500).json({
      error: outcome.error.message,
      category: outcome.error.category,
    });
  }

  res.json({
    data: outcome.result.data,
    metadata: outcome.result.metadata,
    filePath: outcome.result.filePath,
  });
});

Use case: Data-only endpoint. Plugins request this via postMessage; useful for validation, analysis, and structured data access.

Route 3: Live Updates

app.post('/visualizer/update', async (req, res) => {
  const { filePath } = req.body;
  const { error, pluginDataUpdateMessage } = await engine.getUpdatedParsedMetadata(filePath);

  if (error) {
    return res.status(500).json({ error: error.message });
  }

  // Return message for host to post to iframe
  res.json({ updateMessage: pluginDataUpdateMessage, success: true });
});

// File watcher integration (server-side)
fileWatcher.on('change', async (filePath) => {
  const { pluginDataUpdateMessage } = await engine.getUpdatedParsedMetadata(filePath);
  if (pluginDataUpdateMessage) {
    // Send to connected clients via WebSocket
    wss.clients.forEach((client) => {
      client.send(JSON.stringify(pluginDataUpdateMessage));
    });
  }
});

Use case: Push updates to already-loaded iframe when file content changes. Plugins listen via onDataUpdate:

import { onDataUpdate } from '@salesforce/metadata-core-sdk/utils/HostCommunicationUtils';

useEffect(() => {
  const unsubscribe = onDataUpdate((freshData) => {
    setData(freshData); // Re-render with updated data
  });
  return unsubscribe;
}, []);

Step 4 — Wire the browser side

The host's UI mounts an <iframe> whose src is the bundle URL above. Because the iframe is same-origin (everything served from your domain), the bootstrap script the SDK injected can postMessage cleanly to the parent window. The parent UI listens for those messages and proxies REQUEST_PLUGIN_DATA back to the parse endpoint, then posts PLUGIN_DATA_RESPONSE back into the iframe.

This relay logic lives entirely on the host's side. The package gives the iframe a working __ExtensionHostPostMessage and an HTML document; the host decides what listens to those messages on the parent.


Architecture rules enforced by tests

The SDK has two strict architectural rules — both checked at test time:

  1. No runtime dependency on @salesforce/metadata-plugins. Plugin React builds are bundled into the SDK's own dist/plugins/ at build time via scripts/copy-plugin-assets.mjs — same pattern the VS Code adapter uses with webpack CopyPlugin. The plugins package is referenced only at the workspace root's devDependencies. Consumers' npm install never pulls plugins in directly.
  2. No vscode import. This is a browser-host SDK; VS Code-flavored code stays in visualizer-vscode-ext.

Both are enforced by src/__tests__/architecture.test.ts.


Development

npm run build       # tsc + scripts/copy-plugin-assets.mjs (populates dist/plugins/, dist/design-system/)
npm test            # jest with coverage
npm run typecheck   # tsc --noEmit
npm run lint

Build runs from the workspace root — @salesforce/metadata-plugins lives in the root's devDependencies and the build script resolves it from there. Building the package outside the workspace is not supported.

Public API summary

Core Class

  • WebVisualizationEngine — Main class with five methods:
    • getPlugin(metadataFilePath) — Resolve a file path to its registered plugin
    • visualizeMetadata(pluginId) — Generate the plugin UI shell (HTML bundle, no parsing)
    • getParsedMetadata(metadataFilePath) — Parse metadata only
    • getUpdatedParsedMetadata(metadataFilePath) — Generate update message for live updates
    • dispose() — Release resources
  • createWebVisualizationEngine(opts) — Async factory function

Supporting Types

  • HttpAsset<T> — Response wrapper: { contentType, body, cacheControl }
  • PluginInfo — Plugin metadata: { id, displayName, filePatterns, priority? }
  • VisualizeOutcome — Parse result: { result, error }. The error field is a PluginError from @salesforce/metadata-core-sdk.
  • PluginDataUpdateMessage<T = unknown> — Update message for postMessage: { type: 'PLUGIN_DATA_UPDATE', payload: PluginDataResponse<T> }. Generic T lets plugin code with a known data shape opt into type-safety; consumers can narrow on message.type in a discriminated-union switch.
  • WebAdapterOptions — Engine constructor options: { fileSystem, telemetry? }

License

BSD-3-Clause.