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

@route-optimization/core

v1.0.4

Published

Core package for route optimization map visualization

Readme

@route-optimization/core

Core package for route optimization and map visualization. Framework-agnostic TypeScript library providing route optimization via Google Cloud, map adapters, types, and utilities.

🚛 Route Optimization🗺️ Map Visualization🎯 TypeScript🎭 Mock Mode

⚠️ Important: Client-Side vs Server-Side

This package has two entry points:

📦 Main Export (Client-Safe)

import { GoogleMapsAdapter, type Route } from '@route-optimization/core';

✅ Safe for: React, Vue, Next.js client components, browsers
Includes: Map adapters, types, utilities
Excludes: RouteCalculator (uses Node.js gRPC)

🖥️ Server Export (Node.js Only)

import { RouteCalculator } from '@route-optimization/core/server';

✅ Safe for: Node.js, API routes, Next.js server components
❌ Don't use in: React components, browser code
Causes error: Module not found: Can't resolve 'fs'

📖 Read the Client vs Server Guide

Table of Contents

Features

  • 🎯 TypeScript-first: Full type safety with comprehensive type definitions
  • 🚛 Route Optimization: Integrated Google Route Optimization API with mock mode
  • 🗺️ Map Adapters: Pluggable adapter pattern supporting multiple map providers
  • 📦 Zero Dependencies: Pure TypeScript core with no framework dependencies
  • 🧪 Fully Tested: 80%+ test coverage with unit tests
  • 📐 Coordinate Utilities: Distance calculation, bounds calculation, formatting
  • Validation: Built-in validation for routes, stops, and coordinates
  • 🎭 Mock Mode: Test optimization without API calls or credentials

Installation

npm install @route-optimization/core
# or
pnpm add @route-optimization/core
# or
yarn add @route-optimization/core

Quick Start

Route Optimization (5 minutes setup)

⚠️ Server-Side Only: RouteCalculator uses Node.js and cannot run in browsers.
Use this in: API routes, server components, backend services.
Learn more about client vs server usage

// ✅ Server-side code only (Node.js, API routes, etc.)
import { RouteCalculator } from '@route-optimization/core/server';

// 1. Start with mock mode (no credentials needed)
const calculator = new RouteCalculator({ useMockMode: true });
await calculator.initialize();

// 2. Define your optimization problem
const response = await calculator.optimizeRoutes({
  shipments: [
    {
      id: 'delivery-1',
      deliveries: [{ location: { latitude: 13.7467, longitude: 100.5342 } }],
      label: 'Customer A',
    },
    {
      id: 'delivery-2',
      deliveries: [{ location: { latitude: 13.7363, longitude: 100.4918 } }],
      label: 'Customer B',
    },
  ],
  vehicles: [
    {
      id: 'van-1',
      startLocation: { latitude: 13.7563, longitude: 100.5018 },
    },
  ],
});

// 3. Use the optimized routes
if (response.success) {
  console.log('Optimized routes:', response.routes);
  console.log('Total cost:', response.metrics?.totalCost);
}

Ready for production? Replace useMockMode: true with your Google Cloud credentials:

// Using API Key (simpler)
const calculator = new RouteCalculator({
  projectId: 'your-project-id',
  apiKey: 'YOUR_API_KEY',
});

// OR using Service Account (more secure)
const calculator = new RouteCalculator({
  projectId: 'your-project-id',
  credentialsPath: './service-account-key.json',
});

Map Visualization (Client-Safe)

// ✅ Safe for client-side (React, Vue, browsers)
import { GoogleMapsAdapter } from '@route-optimization/core';

const adapter = new GoogleMapsAdapter();
await adapter.initialize({
  apiKey: 'YOUR_GOOGLE_MAPS_API_KEY',
  container: document.getElementById('map'),
});

// Render a route
adapter.renderRoute({
  id: 'route-1',
  vehicleId: 'vehicle-1',
  stops: [
    {
      id: 'depot',
      location: { lat: 13.7563, lng: 100.5018 },
      type: 'START',
      sequence: 0,
    },
  ],
});

When to Use What?

| Scenario | Recommendation | Why | | ---------------------------------- | ---------------------------- | --------------------------- | | Local development | Mock mode | Free, fast, no setup | | Unit testing | Mock mode | Deterministic, no API calls | | Integration testing | Mock mode or staging project | Controlled environment | | Production (< 1000 requests/month) | Real API with free tier | Cost-effective | | Production (high volume) | Real API with optimization | Better routes, traffic data | | Prototyping/demos | Mock mode | Quick to set up |

Setup Guide

Google Cloud Setup (for Production)

To use the Route Optimization API with real data, you need to set up Google Cloud:

1. Create a Google Cloud Project

  1. Go to Google Cloud Console
  2. Create a new project or select existing one
  3. Note your Project ID

2. Enable Required APIs

Enable these APIs in your project:

3. Set Up Billing

Route Optimization API requires billing to be enabled:

  1. Go to Billing
  2. Link a billing account to your project

4. Choose Authentication Method

You have two options for authentication:

Option A: API Key (Simpler, Recommended for Getting Started)
  1. Go to Credentials
  2. Click "Create Credentials" > "API Key"
  3. Copy the API key
  4. (Optional but recommended) Click "Restrict Key" to:
    • Add application restrictions
    • Restrict to Cloud Optimization API

Pros:

  • ✅ Simpler setup
  • ✅ No file management
  • ✅ Easy to rotate

Cons:

  • ⚠️ Less granular permissions
  • ⚠️ Should be restricted properly
Option B: Service Account (More Secure for Production)
  1. Go to IAM & Admin > Service Accounts
  2. Click "Create Service Account"
  3. Name it (e.g., "route-optimizer")
  4. Grant role: "Cloud Optimization API User"
  5. Click "Create Key" and download JSON file
  6. Save as service-account-key.json in your project

Pros:

  • ✅ More secure
  • ✅ Granular IAM permissions
  • ✅ Better audit trail

Cons:

  • ⚠️ More complex setup
  • ⚠️ File management required

5. Initialize RouteCalculator

Using API Key (Recommended for Quick Start)
import { RouteCalculator } from '@route-optimization/core';

const calculator = new RouteCalculator({
  projectId: 'your-project-id',
  apiKey: 'YOUR_API_KEY',
  debug: true, // Enable logging during setup
});

await calculator.initialize();
Using Service Account (File Path)
import { RouteCalculator } from '@route-optimization/core';

const calculator = new RouteCalculator({
  projectId: 'your-project-id',
  credentialsPath: './service-account-key.json',
  debug: true,
});

await calculator.initialize();
Using Service Account (Credentials Object)

Instead of a file path, you can pass credentials directly:

import serviceAccountKey from './service-account-key.json';

const calculator = new RouteCalculator({
  projectId: 'your-project-id',
  credentials: serviceAccountKey,
});

Development Without Google Cloud

Use mock mode for development and testing:

const calculator = new RouteCalculator({
  useMockMode: true,
  debug: true,
});

await calculator.initialize(); // No credentials needed

Mock mode:

  • ✅ No API key or credentials required
  • ✅ No billing charges
  • ✅ Instant responses
  • ✅ Deterministic results for testing
  • ❌ Simplified optimization algorithm
  • ❌ No real-world traffic data

Cost Estimation

Google Route Optimization API pricing (as of 2024):

| Request Size | Price per Request | | ------------------- | --------------------------- | | 0-10 shipments | Free (1,000 requests/month) | | 11-100 shipments | $0.50 | | 101-1,000 shipments | $5.00 | | 1,001+ shipments | Contact sales |

Tips to reduce costs:

  • Use mock mode during development
  • Batch multiple requests together
  • Cache optimization results when possible
  • Set reasonable timeouts to avoid long-running requests
  • Use searchMode: 'RETURN_FAST' for quicker, cheaper results

Usage

Basic Usage with Google Maps

import { GoogleMapsAdapter, StopType } from '@route-optimization/core';
import type { Route } from '@route-optimization/core';

// Initialize the adapter
const mapAdapter = new GoogleMapsAdapter();

await mapAdapter.initialize({
  apiKey: 'YOUR_GOOGLE_MAPS_API_KEY',
  container: 'map-container',
  center: { lat: 13.7563, lng: 100.5018 },
  zoom: 12,
});

// Create a route
const route: Route = {
  id: 'route-1',
  vehicle: {
    id: 'truck-1',
    type: VehicleType.LIGHT_TRUCK,
    startLocation: { lat: 13.7563, lng: 100.5018 },
  },
  stops: [
    {
      id: 'stop-1',
      location: { lat: 13.7467, lng: 100.5342 },
      type: StopType.PICKUP,
      label: 'Pickup Point',
    },
    {
      id: 'stop-2',
      location: { lat: 13.7363, lng: 100.4918 },
      type: StopType.DELIVERY,
      label: 'Delivery Point',
    },
  ],
  totalDistance: 5000,
  totalDuration: 900,
};

// Render the route
mapAdapter.renderRoute(route);

Using Utilities

import {
  calculateBounds,
  calculateDistance,
  formatDistance,
  formatDuration,
  validateRoute,
} from '@route-optimization/core';

// Calculate bounds for multiple points
const bounds = calculateBounds([
  { lat: 13.7563, lng: 100.5018 },
  { lat: 13.7467, lng: 100.5342 },
]);

// Calculate distance between two points
const distance = calculateDistance(
  { lat: 13.7563, lng: 100.5018 },
  { lat: 13.7467, lng: 100.5342 }
);
console.log(formatDistance(distance)); // "3.2 km"

// Validate a route
validateRoute(route); // Throws error if invalid

Route Optimization with RouteCalculator

The RouteCalculator service provides route optimization using Google Route Optimization API with support for mock mode.

import { RouteCalculator } from '@route-optimization/core';
import type { OptimizationRequest, OptimizationResponse } from '@route-optimization/core';

// Initialize with API Key (simplest)
const calculator = new RouteCalculator({
  projectId: 'your-google-cloud-project',
  apiKey: 'YOUR_API_KEY',
  debug: true,
});

// OR initialize with Service Account
const calculatorWithSA = new RouteCalculator({
  projectId: 'your-google-cloud-project',
  credentialsPath: './service-account-key.json',
  debug: true,
});

// OR use mock mode for testing
const mockCalculator = new RouteCalculator({
  useMockMode: true,
  debug: true,
});

// Initialize the calculator
await calculator.initialize();

// Create an optimization request
const request: OptimizationRequest = {
  shipments: [
    {
      id: 'shipment-1',
      pickups: [
        {
          location: { latitude: 13.7563, longitude: 100.5018 },
          duration: '300s',
        },
      ],
      deliveries: [
        {
          location: { latitude: 13.7467, longitude: 100.5342 },
          duration: '300s',
        },
      ],
      label: 'Package A',
      demands: [{ type: 'weight', value: 10 }],
    },
    {
      id: 'shipment-2',
      deliveries: [
        {
          location: { latitude: 13.7363, longitude: 100.4918 },
          duration: '300s',
        },
      ],
      label: 'Package B',
    },
  ],
  vehicles: [
    {
      id: 'vehicle-1',
      startLocation: { latitude: 13.7563, longitude: 100.5018 },
      endLocation: { latitude: 13.7563, longitude: 100.5018 },
      loadLimits: {
        weight: { hardMaxLoad: 50 },
      },
      costPerKilometer: 0.5,
      costPerHour: 10,
    },
  ],
  globalDurationCostPerHour: 20,
  searchMode: 'RETURN_FAST',
  timeout: '30s',
};

// Optimize routes
const response: OptimizationResponse = await calculator.optimizeRoutes(request);

if (response.success) {
  console.log('Optimization successful!');
  console.log('Routes:', response.routes);
  console.log('Metrics:', response.metrics);
  console.log('Statistics:', response.statistics);
} else {
  console.error('Optimization failed:', response.error);
}

RouteCalculator Configuration

interface RouteCalculatorConfig {
  projectId?: string; // Google Cloud project ID
  apiKey?: string; // API Key for authentication (alternative to service account)
  credentialsPath?: string; // Path to service account key file
  credentials?: object; // Service account key JSON object
  useMockMode?: boolean; // Use mock mode (no API calls)
  defaultSearchMode?: 'RETURN_FAST' | 'CONSUME_ALL_AVAILABLE_TIME';
  defaultTimeout?: string; // e.g., "30s"
  debug?: boolean; // Enable debug logging
}

Authentication Options (choose one):

  • apiKey: Simple API key authentication (recommended for getting started)
  • credentialsPath: Path to service account JSON file (more secure)
  • credentials: Service account credentials object (for dynamic loading)
  • useMockMode: true: No authentication needed (for testing)

#### Optimization Request

```typescript
interface OptimizationRequest {
  shipments: Shipment[];
  vehicles: OptimizationVehicle[];
  globalDurationCostPerHour?: number;
  searchMode?: 'RETURN_FAST' | 'CONSUME_ALL_AVAILABLE_TIME';
  timeout?: string;
  considerTraffic?: boolean;
}

interface Shipment {
  id?: string;
  pickups?: PickupDelivery[];
  deliveries?: PickupDelivery[];
  demands?: Array<{ type: string; value: number }>;
  label?: string;
  penaltyCost?: number;
}

interface OptimizationVehicle {
  id?: string;
  startLocation: OptimizationLocation;
  endLocation?: OptimizationLocation;
  loadLimits?: { [resourceType: string]: LoadLimit };
  fixedCost?: number;
  costPerKilometer?: number;
  costPerHour?: number;
  travelMode?: 'DRIVING' | 'WALKING' | 'BICYCLING' | 'TRANSIT';
  maxRouteDuration?: string;
}

Optimization Response

interface OptimizationResponse {
  success: boolean;
  routes?: OptimizedRouteInfo[];
  metrics?: OptimizationMetrics;
  statistics?: RouteStatistics;
  skippedShipments?: SkippedShipment[];
  error?: string;
}

interface OptimizedRouteInfo {
  vehicleId: string;
  shipmentIds: string[];
  totalDistance?: number; // meters
  totalDuration?: string; // e.g., "3600s"
  stops: number;
  cost?: number;
}

interface OptimizationMetrics {
  totalCost: number;
  totalDistance?: number; // meters
  usedVehicles: number;
  skippedShipments: number;
  optimizationDuration?: string;
}

interface RouteStatistics {
  averageStopsPerVehicle: number;
  maxStopsInRoute: number;
  minStopsInRoute: number;
  utilizationRate: number; // 0-1
}

RouteCalculator Methods

  • initialize(): Promise<void> - Initialize the Google Route Optimization client
  • optimizeRoutes(request: OptimizationRequest): Promise<OptimizationResponse> - Optimize routes
  • updateConfig(config: Partial<RouteCalculatorConfig>): void - Update configuration
  • getConfig(): RouteCalculatorConfig - Get current configuration

Mock Mode

Mock mode allows you to test optimization without making actual API calls or setting up Google Cloud credentials:

const calculator = new RouteCalculator({
  useMockMode: true,
  debug: true,
});

await calculator.initialize(); // No API setup needed

const response = await calculator.optimizeRoutes(request);
// Returns simulated optimization results

Error Handling

The calculator provides detailed error messages with helpful tips:

const response = await calculator.optimizeRoutes(request);

if (!response.success) {
  console.error('Error:', response.error);
  // Error messages include troubleshooting tips
  // Example: "API not enabled. Enable it at: https://..."
}

Advanced Examples

Multi-Vehicle Optimization

const request: OptimizationRequest = {
  shipments: [
    { id: 'pkg-1', deliveries: [{ location: { latitude: 13.7467, longitude: 100.5342 } }] },
    { id: 'pkg-2', deliveries: [{ location: { latitude: 13.7363, longitude: 100.4918 } }] },
    { id: 'pkg-3', deliveries: [{ location: { latitude: 13.7263, longitude: 100.5218 } }] },
  ],
  vehicles: [
    {
      id: 'truck-1',
      startLocation: { latitude: 13.7563, longitude: 100.5018 },
      costPerKilometer: 0.8,
      loadLimits: { weight: { hardMaxLoad: 100 } },
    },
    {
      id: 'truck-2',
      startLocation: { latitude: 13.7563, longitude: 100.5018 },
      costPerKilometer: 0.5,
      loadLimits: { weight: { hardMaxLoad: 50 } },
    },
  ],
};

const response = await calculator.optimizeRoutes(request);
response.routes?.forEach((route) => {
  console.log(`${route.vehicleId}: ${route.stops} stops, ${route.totalDistance}m`);
});

With Time Windows

const request: OptimizationRequest = {
  shipments: [
    {
      id: 'urgent-delivery',
      deliveries: [
        {
          location: { latitude: 13.7467, longitude: 100.5342 },
          duration: '300s',
          timeWindows: [
            {
              startTime: '2024-01-15T08:00:00Z',
              endTime: '2024-01-15T10:00:00Z',
            },
          ],
        },
      ],
      penaltyCost: 1000, // High penalty if skipped
    },
  ],
  vehicles: [
    {
      id: 'express-van',
      startLocation: { latitude: 13.7563, longitude: 100.5018 },
      maxRouteDuration: '14400s', // 4 hours max
    },
  ],
};

With Load Capacity

const request: OptimizationRequest = {
  shipments: [
    {
      id: 'heavy-pkg',
      deliveries: [{ location: { latitude: 13.7467, longitude: 100.5342 } }],
      demands: [
        { type: 'weight', value: 30 },
        { type: 'volume', value: 5 },
      ],
    },
  ],
  vehicles: [
    {
      id: 'cargo-truck',
      startLocation: { latitude: 13.7563, longitude: 100.5018 },
      loadLimits: {
        weight: {
          softMaxLoad: 80,
          hardMaxLoad: 100,
          costPerUnitAboveSoftMax: 2,
        },
        volume: {
          hardMaxLoad: 10,
        },
      },
    },
  ],
};

Analyzing Results

const response = await calculator.optimizeRoutes(request);

if (response.success && response.metrics && response.statistics) {
  console.log('=== Optimization Results ===');
  console.log(`Total Cost: $${response.metrics.totalCost.toFixed(2)}`);
  console.log(`Total Distance: ${(response.metrics.totalDistance! / 1000).toFixed(1)} km`);
  console.log(`Vehicles Used: ${response.metrics.usedVehicles}`);
  console.log(`Avg Stops/Vehicle: ${response.statistics.averageStopsPerVehicle.toFixed(1)}`);
  console.log(`Utilization: ${(response.statistics.utilizationRate * 100).toFixed(1)}%`);

  if (response.skippedShipments && response.skippedShipments.length > 0) {
    console.log(`\nSkipped Shipments: ${response.skippedShipments.length}`);
    response.skippedShipments.forEach((s) => {
      console.log(`  - ${s.id}: ${s.reason}`);
    });
  }
}

API Reference

Types

LatLng

interface LatLng {
  lat: number; // -90 to 90
  lng: number; // -180 to 180
}

Stop

interface Stop {
  id: string;
  location: LatLng;
  type: StopType;
  label?: string;
  address?: string;
  sequence?: number;
  timeWindow?: TimeWindow;
  serviceDuration?: number;
  demand?: number;
  metadata?: Record<string, unknown>;
}

Route

interface Route {
  id: string;
  vehicle: Vehicle;
  stops: Stop[];
  segments?: RouteSegment[];
  totalDistance: number;
  totalDuration: number;
  totalCost?: number;
  metrics?: RouteMetrics;
  metadata?: Record<string, unknown>;
}

Map Adapters

IMapAdapter

Interface for map provider adapters.

interface IMapAdapter {
  initialize(config: MapConfig): Promise<void>;
  renderRoute(route: Route, options?: RouteRenderOptions): void;
  renderRoutes(routes: Route[], options?: RouteRenderOptions): void;
  addMarker(stop: Stop, config?: MarkerConfig): string;
  removeMarker(markerId: string): void;
  clearMarkers(): void;
  fitBounds(bounds?: MapBounds): void;
  setCenter(center: LatLng): void;
  setZoom(zoom: number): void;
  clearRoutes(): void;
  getBounds(): MapBounds | null;
  destroy(): void;
  getMapInstance(): unknown;
}

GoogleMapsAdapter

Google Maps implementation of IMapAdapter.

const adapter = new GoogleMapsAdapter({
  events: {
    onClick: (latLng) => console.log('Map clicked:', latLng),
    onMarkerClick: (stop) => console.log('Marker clicked:', stop),
  },
  clustering: true,
});

Utilities

Coordinate Utilities

  • calculateBounds(points: LatLng[]): MapBounds
  • calculateDistance(from: LatLng, to: LatLng): number
  • formatDistance(meters: number, locale?: string): string
  • formatDuration(seconds: number): string
  • isValidCoordinate(coord: LatLng): boolean
  • getBoundsCenter(bounds: MapBounds): LatLng
  • decodePolyline(encoded: string): LatLng[]

Validation Utilities

  • validateRoute(route: Route): void
  • validateStop(stop: Stop): void
  • validateApiKey(apiKey: string): void
  • validateRoutes(routes: Route[]): void

Troubleshooting

RouteCalculator Issues

API Not Enabled Error

Route Optimization API is not enabled.

Solution: Enable the API in Google Cloud Console:

  1. Go to Cloud Optimization API
  2. Select your project
  3. Click "Enable"

Alternative: Use mock mode for testing:

const calculator = new RouteCalculator({ useMockMode: true });

Authentication Failed

Authentication failed. Please check your credentials.

Solutions:

  • Verify your service account key file path is correct
  • Ensure the service account has the "Cloud Optimization API User" role
  • Check that the key file has valid JSON format
  • Try using mock mode to isolate the issue

Billing Error

Billing issue: PERMISSION_DENIED

Solutions:

  • Enable billing for your Google Cloud project
  • Ensure the service account has billing permissions
  • Use mock mode for development without billing

No Routes Returned

If optimization succeeds but no routes are returned:

const response = await calculator.optimizeRoutes(request);
console.log('Skipped shipments:', response.skippedShipments);

Common causes:

  • Vehicle capacity too low for shipment demands
  • Time windows too restrictive
  • Start/end locations too far from shipments
  • Insufficient vehicles for number of shipments

Solutions:

  • Increase vehicle loadLimits
  • Adjust or remove time windows
  • Add penaltyCost to shipments to allow skipping
  • Add more vehicles or reduce shipments

Debug Mode

Enable debug logging to troubleshoot issues:

const calculator = new RouteCalculator({
  debug: true,
  useMockMode: false,
  projectId: 'your-project',
});

await calculator.initialize();
const response = await calculator.optimizeRoutes(request);

// Console will show detailed information:
// 🚀 RouteCalculator initialized
// 🚀 Sending optimization request to Google API...
// 📦 Shipments: 5
// 🚛 Vehicles: 2
// ✅ Received response in 1234ms

Map Adapter Issues

Google Maps Not Loading

Ensure you:

  1. Have a valid Google Maps API key
  2. Enabled "Maps JavaScript API" in Google Cloud Console
  3. Set up billing for your project
  4. Added API key restrictions if needed

Markers Not Appearing

Check that:

  • Stop coordinates are valid (lat: -90 to 90, lng: -180 to 180)
  • Map is fully initialized before adding markers
  • Container element exists in DOM

Development

# Install dependencies
pnpm install

# Build
pnpm build

# Test
pnpm test

# Test with coverage
pnpm test -- --coverage

# Lint
pnpm lint

Bundle Size

  • Core: < 5KB (gzipped)
  • Zero runtime dependencies
  • Tree-shakeable: Only import what you need

Browser Support

  • Chrome >= 90
  • Firefox >= 88
  • Safari >= 14
  • Edge >= 90

License

MIT

Related Packages