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

@smarthivelabs-devs/geocore-core

v1.6.6

Published

Geocore universal SDK — geocoding, routing, places for browser and Node.js

Readme

@smarthivelabs-devs/geocore-core

Universal geospatial SDK for browser and Node.js. One API across 8 providers, 14 geo-operations, weather, and the SmartHive Pricing Engine — with automatic fallback, circuit breaker, rate limiting, and caching built in.

Installation

npm install @smarthivelabs-devs/geocore-core
# or
pnpm add @smarthivelabs-devs/geocore-core

Map rendering libs (mapbox-gl, @googlemaps/js-api-loader, @googlemaps/markerclusterer) are bundled as dependencies and auto-install.


Choosing a Mode

The SDK has two completely separate clients — pick the right one first:

| | Geocore (direct) | BackendClient (backend proxy) | |---|---|---| | API keys location | In your app | On your Geocore server | | Fallback | Client-side, automatic | Server-side | | Circuit breaker | Per-provider, in-app | Server handles it | | Provider pinning | { provider: 'mapbox' } | { provider: 'mapbox' } | | Weather / Pricing | ✗ | ✓ | | Use when | Prototyping, CLI tools, SSR | Production apps, mobile, multi-tenant |


Two Modes

Direct Mode — API keys in your app

Provider keys live in the client. Best for prototyping and tools with no backend.

import { Geocore } from '@smarthivelabs-devs/geocore-core';

const geocore = new Geocore({
  apiKeys: {
    mapbox: { secretToken: 'sk.eyJ1...', publicToken: 'pk.eyJ1...' },
    google: 'AIza...',
    tomtom: 'abc123',
  },
  provider: {
    preferredOrder: ['mapbox', 'google', 'tomtom'],
    fallbackEnabled: true,
    timeout: 8000,
  },
});

Backend Mode — Geocore API proxy

API keys stay on your server. The SDK talks to your Geocore backend, which handles provider fallback. Weather and pricing only work in this mode.

const geocore = new Geocore({
  apiKey: 'your-geocore-api-key',  // apiUrl optional — defaults to production
});

Normalized Address Components

Every geocode and reverse-geocode result now returns normalized components regardless of which provider answered. No more guessing whether the city lives in city, locality, place, or municipality.

import type { AddressComponents } from '@smarthivelabs-devs/geocore-core';

// components shape — same keys from every provider:
interface AddressComponents {
  streetNumber?: string;  // house / building number
  street?: string;        // road / street name
  area?: string;          // neighborhood, suburb, or quarter
  city?: string;          // city, town, or village
  county?: string;        // county or local district
  state?: string;         // state, region, or province
  country?: string;       // full country name
  countryCode?: string;   // ISO 3166-1 alpha-2 (e.g. "GH")
  postalCode?: string;
  poiName?: string;       // point-of-interest or establishment name
}

getDisplayAddress(components, fallback?)

Build a clean, human-readable display string from any AddressComponents object. Automatically orders parts from specific → general and removes duplicates.

import { getDisplayAddress } from '@smarthivelabs-devs/geocore-core';

const rev = await geocore.reverse(6.6885, -1.6244);
// rev.data.components → { area: 'Adum', city: 'Kumasi', state: 'Ashanti Region', country: 'Ghana' }

const label = getDisplayAddress(rev.data.components);
// → "Adum, Kumasi, Ashanti Region, Ghana"

const label = getDisplayAddress(rev.data.components, 'Unknown location');
// → fallback used when components are empty

Geo Operations (14)

Geocoding

// Address → coordinates
const result = await geocore.geocode('1600 Amphitheatre Parkway, Mountain View, CA');
console.log(result.data.coordinates);           // { lat: 37.4224, lng: -122.0840 }
console.log(result.data.components?.city);      // 'Mountain View'
console.log(result.data.components?.state);     // 'California'
console.log(result.provider);                   // 'mapbox'

// Coordinates → address (normalized components, all providers)
const rev = await geocore.reverse(37.4224, -122.0840);
console.log(rev.data.formattedAddress);
console.log(rev.data.components?.city);         // 'Mountain View'
console.log(rev.data.components?.area);         // neighborhood if available
console.log(getDisplayAddress(rev.data.components));

Directions

const route = await geocore.directions(
  { lat: 51.5074, lng: -0.1278 },   // from
  { lat: 48.8566, lng: 2.3522 },    // to
  { mode: 'driving' }
);
console.log(route.data.distance.text);   // '341 km'
console.log(route.data.duration.text);   // '3 hrs 15 min'
console.log(route.data.steps);

Places Search

const places = await geocore.places('coffee', { lat: 51.5074, lng: -0.1278 }, { radius: 1000 });
const nearby  = await geocore.nearbySearch('restaurant', { lat: 51.5074, lng: -0.1278 });
const suggest = await geocore.autocomplete('Hyde Park Lo');

Elevation, Timezone, Traffic

const elev = await geocore.elevation(51.5074, -0.1278);
console.log(elev.data.elevation); // metres above sea level

const tz = await geocore.timezone(51.5074, -0.1278);
console.log(tz.data.timeZoneId); // 'Europe/London'

const traffic = await geocore.traffic(51.5074, -0.1278);
console.log(traffic.data.congestionLevel); // 'light'

Isochrone, Distance Matrix, Snap to Road, Route Optimize

// Reachability polygon (15 & 30 min drive)
const iso = await geocore.isochrone(51.5074, -0.1278, [15, 30], { mode: 'driving' });

// Distance/duration grid
const matrix = await geocore.distanceMatrix(
  ['London, UK', 'Paris, France'],
  ['Berlin, Germany', 'Madrid, Spain'],
);

// Snap GPS points to road network
const snapped = await geocore.snapToRoad([
  { lat: 51.501, lng: -0.124 },
  { lat: 51.505, lng: -0.120 },
]);

// Optimise waypoint order (TSP)
const optimised = await geocore.routeOptimize([
  'London, UK', 'Paris, France', 'Berlin, Germany',
]);

// Static map image URL
const map = await geocore.staticMap(51.5074, -0.1278, { zoom: 14, width: 800, height: 600 });

Weather (backend mode required)

const geocore = new Geocore({ apiKey: 'your-key' });

// Current conditions
const now = await geocore.weather(51.5074, -0.1278);
console.log(now.data.temperature);   // °C
console.log(now.data.description);   // 'Partly cloudy'
console.log(now.data.humidity);      // 72

// 7-day forecast (max 14 days)
const forecast = await geocore.forecast(51.5074, -0.1278, 7);
forecast.data.days.forEach(day => {
  console.log(`${day.date}: ${day.tempMin}–${day.tempMax}°C, ${day.description}`);
});

Weather providers: Open-Meteo (free, no key) → OpenWeatherMap (free tier fallback).


SmartHive Pricing Engine (backend mode required)

The Pricing Engine converts a base price into the user's local currency using stored exchange rates — no per-request API calls. Rates are refreshed automatically on the 1st and 15th of each month.

Location-based pricing

const geocore = new Geocore({ apiKey: 'your-key' });

// User in Nigeria — Hivedemia Pro priced at 100 GHS
const price = await geocore.localizePrice(100, 'GHS', 6.5244, 3.3792);
console.log(price.data);
// {
//   amount: 4500,
//   currency: 'NGN',
//   symbol: '₦',
//   country: 'NG',
//   originalAmount: 100,
//   originalCurrency: 'GHS',
//   rate: 45.0,
// }

Manual country override (country picker UI)

// User manually selects United States
const price = await geocore.localizePriceByCountry(100, 'GHS', 'US');
// { amount: 9.99, currency: 'USD', symbol: '$', country: 'US' }

Direct FX lookup

const rate  = await geocore.fxRate('USD', 'EUR');
console.log(rate.data.rate); // 0.92

const rates = await geocore.fxRates('USD');
console.log(rates.data.rates); // { EUR: 0.92, GBP: 0.79, NGN: 1580, ... }

Provider Capability Table

| Provider | Geocode | Reverse | Autocomplete | Places | Nearby | Directions | Distance Matrix | Elevation | Isochrone | Traffic | Timezone | Snap to Road | Route Optimize | Static Map | Weather | |----------|:-------:|:-------:|:------------:|:------:|:------:|:----------:|:---------------:|:---------:|:---------:|:-------:|:--------:|:------------:|:--------------:|:----------:|:-------:| | Mapbox | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — | ✓ | ✓ | ✓ | — | | Google Maps | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — | ✓ | ✓ | ✓ | — | ✓ | — | | TomTom | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | ✓ | — | — | — | ✓ | — | | HERE | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | ✓ | ✓ | — | — | — | — | | Azure Maps | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | ✓ | ✓ | — | — | ✓ | — | | LocationIQ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | — | — | — | — | — | ✓ | — | | OpenRouteService | ✓ | ✓ | — | — | — | ✓ | ✓ | ✓ | ✓ | — | — | ✓ | ✓ | — | — | | Geoapify | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — | ✓ | — | — | — | — | ✓ | — | | Open-Meteo | — | — | — | — | — | — | — | — | — | — | — | — | — | — | ✓ | | OpenWeatherMap | — | — | — | — | — | — | — | — | — | — | — | — | — | — | ✓ |

Provider Selection

Per-call provider pinning — force a specific provider for one call, bypassing the fallback chain:

// Direct mode — uses Mapbox even if Google is first in preferredOrder
const result = await geocore.geocode('Accra Mall, Ghana', { provider: 'mapbox' });
const rev    = await geocore.reverse(5.636, -0.174, { provider: 'google' });

// Backend mode — passes ?provider=mapbox to the Geocore API
const result = await client.geocode('Accra Mall', { provider: 'here' });
const rev    = await client.reverse(5.636, -0.174, { provider: 'locationiq' });

If the requested provider is not configured or its key is invalid, it throws a GeocoreError with code PROVIDER_ERROR — it will not silently fall back to another provider when you explicitly pinned one.


Fallback Mechanism

Direct mode — fully automatic, client-side:

const geocore = new Geocore({
  apiKeys: {
    mapbox: { secretToken: 'sk...' },
    google: 'AIza...',
    here: 'your-key',
  },
  providers: {
    // Tried in this order — next is used only if current fails
    preferredOrder: ['mapbox', 'google', 'here'],
    fallbackEnabled: true,   // default: true
    timeout: 8000,
  },
});

const result = await geocore.geocode('Kumasi, Ghana');
console.log(result.provider);      // which provider actually answered
console.log(result.fallbackUsed);  // true if primary failed and fallback was used

Fallback triggers on: network errors, timeouts, rate limits, zero results, any exception.

To disable fallback (fail fast, use only the primary provider):

providers: { preferredOrder: ['mapbox'], fallbackEnabled: false }

Backend mode — server-side. The client always calls one endpoint. The Geocore server handles the fallback chain internally (configured via its env vars). Each response still includes provider so you know who answered.


Fallback Chain (default order)

mapbox → google → tomtom → here → azure → locationiq → openrouteservice → geoapify

Only providers with valid API keys are actually registered — unconfigured providers are skipped transparently.


Provider Environment Variables (backend / server-side)

| Provider | Env Var | |----------|---------| | Mapbox | MAPBOX_PUBLIC_TOKEN, MAPBOX_SECRET_TOKEN | | Google Maps | GOOGLE_API_KEY | | TomTom | TOMTOM_API_KEY | | HERE | HERE_API_KEY | | Azure Maps | AZURE_API_KEY | | LocationIQ | LOCATIONIQ_API_KEY | | OpenRouteService | OPENROUTESERVICE_API_KEY | | Geoapify | GEOAPIFY_API_KEY | | OpenWeatherMap | OPENWEATHER_API_KEY (optional — Open-Meteo is the free fallback) |


Error Handling

import { GeocoreError } from '@smarthivelabs-devs/geocore-core';

try {
  const result = await geocore.geocode('123 Fake Street');
} catch (err) {
  if (err instanceof GeocoreError) {
    console.log(err.code);         // 'PROVIDER_ERROR' | 'TIMEOUT' | 'NO_RESULTS' | ...
    console.log(err.provider);     // which provider failed
    console.log(err.statusCode);   // HTTP status if applicable
  }
}

Error codes: PROVIDER_ERROR · TIMEOUT · RATE_LIMIT_EXCEEDED · CIRCUIT_BREAKER_OPEN · NO_RESULTS · INVALID_COORDINATES · NETWORK_ERROR · VALIDATION_ERROR


BackendClient (direct backend integration)

If you only need the HTTP layer without the full Geocore class (e.g. in a server component or a thin wrapper):

import { BackendClient } from '@smarthivelabs-devs/geocore-core';

const client = new BackendClient({
  apiKey: 'your-geocore-api-key',
  // apiUrl defaults to https://geocore.smarthivelabs.dev/api
  timeout: 10000,
  logger: { enabled: true, level: 'debug' },
});

const result = await client.geocode('Accra, Ghana');
const weather = await client.weather(5.6037, -0.1870);
const price   = await client.localizePrice(100, 'GHS', { lat: 6.5244, lng: 3.3792 });

// Provider registry (no auth)
const providers = await client.getProviders();
const supported = await client.getProvidersForOperation('directions');

Cache & Resilience

const geocore = new Geocore({
  apiKeys: { mapbox: { secretToken: '...' } },
  resilience: {
    cache: {
      enabled: true,
      ttl: {
        geocode: 86400,      // 24 hours
        reverse: 86400,
        directions: 3600,    // 1 hour
        places: 1800,        // 30 min
        traffic: 30,         // 30 seconds
      },
    },
    circuitBreaker: {
      failureThreshold: 5,   // open after 5 consecutive failures
      resetTimeout: 30000,   // retry after 30 seconds
    },
    rateLimit: {
      requestsPerSecond: 10,
    },
    retry: {
      maxAttempts: 3,
      initialDelayMs: 1000,
    },
  },
});

// Toggle cache at runtime
geocore.setCache(false);                              // disable
geocore.setCache(true, { traffic: 60 });              // enable with custom TTL

// Inspect provider + circuit breaker health
const status = geocore.getStatus();
console.log(status.providers);   // [{ name, status, failures }]
console.log(status.cache);       // { enabled, size }

Interactive Map (browser)

const map = await geocore.createMap({
  container: 'map',       // DOM element ID
  center: [51.5074, -0.1278],
  zoom: 12,
});

// Add markers
map.addMarker({ lat: 51.5074, lng: -0.1278, title: 'London' });

// Draw route polyline
map.addPolyline({ coordinates: route.data.polyline, color: '#3B82F6' });

TypeScript

All methods are fully typed. Key exports:

import type {
  GeocodingResult, ReverseGeocodingResult,
  DirectionsResult, RouteOptimizeResult, SnapToRoadResult,
  PlacesResult, NearbySearchResult, AutocompleteResult,
  ElevationResult, IsochroneResult, TimezoneResult,
  DistanceMatrixResult, TrafficResult, StaticMapResult,
  WeatherCurrentResult, WeatherForecastResult,
  PricingResult, FxRateResult, FxRatesResult,
  GeocodingOptions, DirectionsOptions, PlacesOptions,
  WeatherOptions, PricingOptions,
  AddressComponents,          // normalized address fields
  Coordinates, SDKResponse, GeocoreConfig,
} from '@smarthivelabs-devs/geocore-core';

// Value export (not type-only)
import { getDisplayAddress } from '@smarthivelabs-devs/geocore-core';

Changelog

1.6.0

  • Normalized components — all providers now return the same standard keys (streetNumber, street, area, city, county, state, country, countryCode, postalCode, poiName). Previously each provider used different field names.
  • Fixed Mapbox reverse geocoding — was storing full place names (e.g. "Kumasi, Ashanti Region, Ghana") in each component key instead of just the specific level name (e.g. "Kumasi").
  • Added getDisplayAddress(components, fallback?) — utility that builds a clean, deduplicated display string from any AddressComponents object.

License

MIT — SmartHive Labs