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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@expozr/navigator

v2.2.0

Published

Universal runtime loader for the Expozr ecosystem

Readme

@expozr/navigator

Universal runtime loader for the Expozr ecosystem, providing seamless dynamic module loading and runtime sharing capabilities.

📋 Table of Contents

🌟 Overview

The Navigator package provides the ExpozrNavigator - a full-featured runtime loader for the Expozr ecosystem with advanced module system support.

The ExpozrNavigator extends the BaseExpozrNavigator class that provides core functionality like caching, event handling, and inventory management.

✨ ExpozrNavigator Features

  • 🎯 Smart Format Detection: Automatically detects and loads the best available module format (ESM, UMD, CJS)
  • 🔄 Multi-Format Support: Tries multiple formats in order of preference (ESM → UMD → CJS → Auto)
  • 🧠 Intelligent Fallbacks: If one format fails, automatically tries alternatives
  • ⚡ Advanced Module System: Integration with Expozr's global module system
  • 🛡️ Error Recovery: Comprehensive error handling with detailed feedback
  • 📊 Format Tracking: Tracks which format was successfully loaded
  • 🎪 Strategy Awareness: Reports loading strategy used (enhanced/fallback)

📦 Installation

npm install @expozr/navigator

🚀 Quick Start

import { createNavigator } from "@expozr/navigator";

const navigator = createNavigator({
  expozrs: {
    components: {
      url: "http://localhost:3001",
      version: "^1.0.0",
    },
  },
  cache: {
    strategy: "memory",
    ttl: 300000, // 5 minutes
  },
  // Module system configuration
  moduleSystem: {
    primary: "esm", // Prefer ESM modules
    fallbacks: ["umd", "cjs"], // Fall back to UMD, then CJS
    strategy: "dynamic", // Use dynamic loading strategy
    hybrid: true, // Enable hybrid mode
  },
});

// Load a remote module with default settings
const buttonModule = await navigator.loadCargo("components", "Button");
const { Button } = buttonModule.module;

// Load with per-component module preferences
const advancedModule = await navigator.loadCargo(
  "components",
  "AdvancedButton",
  {
    moduleFormat: "umd", // Prefer UMD for this specific load
    fallbackFormats: ["esm"], // Fall back to ESM if UMD fails
    strategy: "eager", // Load eagerly
  }
);

🔀 Navigator Types

🚀 EnhancedNavigator (Default)

The EnhancedNavigator is the recommended choice for most applications. It provides advanced features and robust module loading capabilities.

✨ Features

  • 🎯 Smart Format Detection: Automatically detects and loads the best available module format (ESM, UMD, CJS)
  • 🔄 Multi-Format Support: Tries multiple formats in order of preference (ESM → UMD → CJS → Auto)
  • 🧠 Intelligent Fallbacks: If one format fails, automatically tries alternatives
  • ⚡ Advanced Module System: Integration with Expozr's global module system with configurable preferences
  • 🛡️ Error Recovery: Comprehensive error handling with detailed feedback
  • 📊 Format Tracking: Tracks which format was successfully loaded
  • 🎪 Strategy Awareness: Reports loading strategy used (dynamic/static/lazy/eager)
  • ⚙️ Flexible Configuration: Global and per-load module system configuration
  • 🎛️ Loading Strategy Control: Choose between dynamic, static, lazy, or eager loading strategies

📚 API Reference

Factory Functions

createNavigator(config?)

Creates an ExpozrNavigator instance.

function createNavigator(config?: NavigatorConfig): ExpozrNavigator;

Parameters:

  • config.expozrs: Record<string, ExpozrReference> - Expozr configurations
  • config.cache?: CacheConfig - Cache configuration
  • config.loading?: LoadingConfig - Loading timeout and retry settings
  • config.moduleSystem?: ModuleSystemConfig - Module system configuration

Navigator Methods

loadCargo<T>(expozr, cargo, options?)

Load a module from a expozr.

async loadCargo<T = any>(
  expozr: string,
  cargo: string,
  options?: LoadOptions
): Promise<LoadedCargo<T>>

Returns: LoadedCargo<T> with the following properties:

  • module: T - The loaded module
  • cargo: Cargo - Cargo metadata
  • expozr: ExpozrInfo - Expozr information
  • loadedAt: number - Timestamp of loading
  • fromCache: boolean - Whether loaded from cache
  • format: ModuleFormat - Format used
  • strategy: ModuleLoadingStrategy - Strategy used

Other Methods

  • isCargoLoaded(expozr, cargo): boolean - Check if cargo is loaded
  • getLoadedCargo(cacheKey): LoadedCargo | undefined - Get cached cargo
  • reset(): Promise<void> - Reset navigator and clear caches
  • getCacheStats(): object - Get cache statistics

⚙️ Configuration

Navigator Configuration

interface NavigatorConfig {
  expozrs: Record<string, ExpozrReference>;
  cache?: CacheConfig;
  loading?: LoadingConfig;
  moduleSystem?: ModuleSystemConfig;
}

Expozr Reference

interface ExpozrReference {
  url: string; // Expozr base URL
  version?: string; // Version constraint (default: "^1.0.0")
}

Cache Configuration

interface CacheConfig {
  strategy: "memory" | "localStorage" | "indexedDB" | "none";
  ttl?: number; // Time to live in milliseconds
  maxSize?: number; // Maximum cache size
}

Module System Configuration

interface ModuleSystemConfig {
  primary?: "esm" | "umd" | "cjs"; // Primary module format to prefer
  fallbacks?: ("esm" | "umd" | "cjs")[]; // Fallback formats if primary fails
  strategy?: "dynamic" | "static" | "lazy" | "eager"; // Loading strategy
  hybrid?: boolean; // Whether to enable hybrid loading
}

Load Options

interface LoadOptions {
  timeout?: number; // Override default timeout
  retry?: RetryConfig; // Override default retry config
  cache?: boolean; // Whether to use cache
  fallback?: () => Promise<any>; // Fallback module if loading fails
  exports?: string[]; // Specific exports to extract
  cacheBusting?: boolean; // Whether to add cache busting parameters

  // Module system preferences (per-load overrides)
  moduleFormat?: "esm" | "umd" | "cjs"; // Module format preference
  fallbackFormats?: ("esm" | "umd" | "cjs")[]; // Fallback formats
  strategy?: "dynamic" | "static" | "lazy" | "eager"; // Loading strategy override
}

💾 Cache Strategies

  • memory - In-memory cache (default, fastest)
  • localStorage - Browser localStorage (persistent)
  • indexedDB - Browser IndexedDB (persistent, large data)
  • none - No caching (always fetch)

⚙️ Module System Configuration

The Navigator provides flexible module loading with support for different formats and strategies.

Global Configuration

Set default preferences when creating the navigator:

const navigator = createNavigator({
  expozrs: {
    /* ... */
  },
  moduleSystem: {
    primary: "esm", // Prefer ES modules
    fallbacks: ["umd", "cjs"], // Try UMD, then CJS if ESM fails
    strategy: "dynamic", // Use dynamic loading
    hybrid: true, // Enable hybrid mode for best compatibility
  },
});

Per-Load Configuration

Override settings for specific loads:

// Load with ESM preference
const esmModule = await navigator.loadCargo("remote", "Button", {
  moduleFormat: "esm",
  fallbackFormats: ["umd"],
  strategy: "dynamic",
});

// Load with UMD preference
const umdModule = await navigator.loadCargo("remote", "Button", {
  moduleFormat: "umd",
  fallbackFormats: ["esm", "cjs"],
  strategy: "eager",
});

Module Formats

  • ESM (esm) - ES Modules (modern, tree-shakeable, best performance)
  • UMD (umd) - Universal Module Definition (browser + Node.js compatibility)
  • CJS (cjs) - CommonJS (Node.js compatibility)

Loading Strategies

  • Dynamic (dynamic) - Load when needed (default, balances performance and memory)
  • Static (static) - Load at build time (fastest runtime, larger bundles)
  • Lazy (lazy) - Lazy loading with code splitting (smallest initial bundle)
  • Eager (eager) - Load immediately when referenced (fastest subsequent access)

Smart Defaults

The Navigator uses intelligent defaults:

  1. Format Preference: ESM → UMD → CJS → Auto-detect
  2. Strategy: Dynamic loading for best balance
  3. Fallbacks: Automatic format fallback on failure
  4. Error Recovery: Graceful degradation with detailed error reporting

🔄 Module Loaders

The package includes several module loaders:

  • UniversalModuleLoader - Auto-detects environment (Browser/Node.js)
  • BrowserModuleLoader - Optimized for browser environments
  • NodeModuleLoader - Optimized for Node.js environments
  • UMDModuleLoader - Specialized UMD module loading

🤖 Auto-Loader

For simplified usage, the package provides an auto-loader utility:

import { createAutoLoader } from "@expozr/navigator";

const loader = await createAutoLoader({
  expozrs: {
    "math-utils": {
      url: "http://localhost:3001",
      modules: {
        calculator: "calculator",
        advanced: "advanced",
      },
    },
  },
  autoExpose: true, // Expose functions globally
  globalNamespace: "mathApp", // Global namespace (default: "expozr")
});

// Functions are automatically available
const result = mathApp.add(5, 3);

💡 Examples

Loading React Components

import React from "react";
import { createNavigator } from "@expozr/navigator";

const navigator = createNavigator({
  expozrs: {
    "ui-components": {
      url: "http://localhost:3001"
    }
  },
  // Global module system preferences
  moduleSystem: {
    primary: "esm",
    fallbacks: ["umd", "cjs"],
    strategy: "dynamic",
    hybrid: true,
  },
});

// Component with ESM preference
const RemoteButton = React.lazy(() =>
  navigator.loadCargo("ui-components", "Button", {
    moduleFormat: "esm",
    fallbackFormats: ["umd"],
    strategy: "dynamic",
  })
    .then(cargo => ({ default: cargo.module.Button }))
);

// Component with UMD preference (for legacy compatibility)
const LegacyComponent = React.lazy(() =>
  navigator.loadCargo("ui-components", "LegacyWidget", {
    moduleFormat: "umd",
    fallbackFormats: ["esm"],
    strategy: "eager",
  })
    .then(cargo => ({ default: cargo.module.LegacyWidget }))
);

function App() {
  return (
    <div>
      <React.Suspense fallback={<div>Loading ESM button...</div>}>
        <RemoteButton variant="primary">ESM Button</RemoteButton>
      </React.Suspense>

      <React.Suspense fallback={<div>Loading legacy component...</div>}>
        <LegacyComponent />
      </React.Suspense>
    </div>
  );
}

Using with TypeScript

interface MathModule {
  add(a: number, b: number): number;
  multiply(a: number, b: number): number;
}

const mathCargo = await navigator.loadCargo<MathModule>("utils", "math");
const { add, multiply } = mathCargo.module;

const sum = add(5, 3); // TypeScript knows the signature
const product = multiply(4, 7); // Full type safety

Error Handling

try {
  const module = await navigator.loadCargo("remote", "nonexistent");
} catch (error) {
  if (error instanceof CargoNotFoundError) {
    console.log(`Cargo not found: ${error.cargo}`);
  } else if (error instanceof ExpozrNotFoundError) {
    console.log(`Expozr not found: ${error.expozr}`);
  } else {
    console.log("Loading failed:", error.message);
  }
}

🔧 Troubleshooting

Common Issues

1. Module Not Loading

Problem: Module fails to load with network errors.

Solution:

  • Check if the expozr URL is correct and accessible
  • Verify CORS settings on the remote server
  • Use EnhancedNavigator for better format fallbacks

2. Cache Issues

Problem: Modules not updating after changes.

Solution:

// Clear cache and reload
await navigator.reset();
const freshModule = await navigator.loadCargo("remote", "module");

3. Format Compatibility

Problem: Module loads but exports are undefined.

Solution:

  • Use EnhancedNavigator for automatic format detection
  • Check the module's export format in the expozr inventory
  • Verify the module is properly built for your target environment

4. Performance Issues

Problem: Slow loading times.

Solution:

  • Use appropriate cache strategy (memory for speed, localStorage for persistence)
  • Choose optimal loading strategy (eager for critical components, lazy for optional ones)
  • Implement module preloading for critical paths
  • Prefer ESM format for better tree-shaking and performance

5. Module Format Issues

Problem: Module loads but functions/components are undefined.

Solution:

// Check what format was actually loaded
const cargo = await navigator.loadCargo("remote", "Button");
console.log(
  `Loaded using ${cargo.format} format with ${cargo.strategy} strategy`
);

// Try different format preferences
const cargo = await navigator.loadCargo("remote", "Button", {
  moduleFormat: "umd", // Try UMD if ESM fails
  fallbackFormats: ["esm", "cjs"],
});

6. Loading Strategy Problems

Problem: Components loading too slowly or causing bundle bloat.

Solution:

// For critical components - use eager loading
const criticalCargo = await navigator.loadCargo("remote", "Header", {
  strategy: "eager",
});

// For optional components - use lazy loading
const optionalCargo = await navigator.loadCargo("remote", "Sidebar", {
  strategy: "lazy",
});

Debug Mode

Enable debug logging:

// Enable debug logs in development
if (process.env.NODE_ENV === "development") {
  // EnhancedNavigator shows detailed format attempts
  const navigator = createNavigator({ enhanced: true });
}

Performance Monitoring

const stats = navigator.getCacheStats();
console.log("Cache performance:", stats);

// Navigator provides detailed format and strategy information
const cargo = await navigator.loadCargo("remote", "module");
console.log(`Loaded ${cargo.format} format using ${cargo.strategy} strategy`);

// Monitor loading performance by format
const startTime = Date.now();
const esmCargo = await navigator.loadCargo("remote", "Button", {
  moduleFormat: "esm",
});
console.log(`ESM loading took: ${Date.now() - startTime}ms`);

// Compare different strategies
const dynamicStart = Date.now();
const dynamicCargo = await navigator.loadCargo("remote", "Component", {
  strategy: "dynamic",
});
console.log(`Dynamic loading: ${Date.now() - dynamicStart}ms`);

const eagerStart = Date.now();
const eagerCargo = await navigator.loadCargo("remote", "Component", {
  strategy: "eager",
});
console.log(`Eager loading: ${Date.now() - eagerStart}ms`);

🤝 Contributing

See CONTRIBUTING.md for contribution guidelines.

📄 License

MIT - see LICENSE for details.