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

rendezvous-kit

v1.21.1

Published

Find fair meeting points for N people — isochrone intersection, venue search, and fairness scoring.

Readme

rendezvous-kit

Find fair meeting points for N people — isochrone intersection, venue search, and fairness scoring.

npm licence TypeScript Nostr

Live Demo →

How it works

  Alice (London)        Bob (Bristol)        Carol (Birmingham)
       ╲                     │                     ╱
    isochrone            isochrone            isochrone
       ╲                     │                     ╱
        ╲────────── intersection ──────────╱
                         │
                   venue search
                         │
                  fairness scoring
                         │
                 ranked suggestions

Geographic midpoint tools find the centre of a triangle. rendezvous-kit finds where everyone can actually get to — using real road networks, travel times, and venue availability.

Why rendezvous-kit?

  • Full pipeline — isochrone computation → polygon intersection → venue search → fairness scoring, all in one library
  • Engine-agnostic — bring your own routing engine: Valhalla, OpenRouteService, GraphHopper, or OSRM
  • Fairness strategiesmin_max (minimise worst case), min_total (minimise sum), min_variance (equalise travel times)
  • Built on geohash-kit — leverages our spatial primitives; only runtime dependency
  • Zero third-party dependencies — ships with a pure-TypeScript Sutherland–Hodgman polygon intersection, Overpass API venue search, and all engine adapters

Use Cases

  • Social apps — "Meet in the middle" features for groups of friends picking a pub, café, or restaurant
  • Ride-sharing — optimal pickup points that minimise detour for all passengers
  • Event planning — conference venues fair to attendees from multiple cities
  • Corporate — office locations that balance commute times across a distributed team
  • Delivery logistics — hub placement that minimises worst-case last-mile distances
  • Emergency services — staging areas reachable by multiple response units within a time budget

Compared to Alternatives

| Capability | Geographic midpoint libraries | Commercial APIs (Targomo, TravelTime) | rendezvous-kit | |---|---|---|---| | Travel-time aware | No — straight-line distance only | Yes | Yes | | Multi-person intersection | No | Yes (API key + billing) | Yes (self-hosted, free) | | Fairness weighting | No | No | Yes — 3 strategies | | Venue search included | No | No | Yes — via Overpass API | | Engine-agnostic | N/A | No — vendor lock-in | Yes — 4 engines | | Works offline/self-hosted | N/A | No | Yes (with local Valhalla/OSRM) |

rendezvous-kit is the only open-source library that combines isochrone intersection, venue discovery, and fairness scoring in a single pipeline.

Install

npm install rendezvous-kit

Quick Start

import { findRendezvous } from 'rendezvous-kit'
import { ValhallaEngine } from 'rendezvous-kit/engines/valhalla'

const engine = new ValhallaEngine({ baseUrl: 'http://localhost:8002' })

const suggestions = await findRendezvous(engine, {
  participants: [
    { lat: 51.5074, lon: -0.1278, label: 'Alice' },  // London
    { lat: 51.4545, lon: -2.5879, label: 'Bob' },    // Bristol
    { lat: 52.4862, lon: -1.8904, label: 'Carol' },  // Birmingham
  ],
  mode: 'drive',
  maxTimeMinutes: 90,
  venueTypes: ['cafe', 'restaurant'],
  fairness: 'min_max',
  limit: 5,
})

for (const s of suggestions) {
  console.log(`${s.venue.name} — score: ${s.fairnessScore.toFixed(1)} min`)
  console.log('  Travel times:', s.travelTimes)
}

Engine Support

| Engine | Isochrone | Route Matrix | Auth | |--------|:---------:|:------------:|------| | Valhalla | Yes | Yes | None (self-hosted) | | OpenRouteService | Yes | Yes | API key | | GraphHopper | Yes | Yes | API key (optional) | | OSRM | No | Yes | None (self-hosted) |

OSRM does not support isochrone computation — use it only when you need a fast route matrix and are supplying your own intersection polygon.

Fairness Strategies

| Strategy | Optimises | Use when... | |----------|-----------|-------------| | min_max | Worst-case travel time | You want nobody to travel excessively | | min_total | Sum of all travel times | You want minimum total travel for the group | | min_variance | Variance in travel times | You want everyone to travel roughly equally |

API Reference

Core function

| Function | Description | |----------|-------------| | findRendezvous(engine, options) | Run the full pipeline and return ranked suggestions |

Geometry (rendezvous-kit/geo)

| Function | Description | |----------|-------------| | intersectPolygons(polygons) | Sutherland–Hodgman N-polygon intersection; returns largest component or null | | intersectPolygonsAll(polygons) | N-polygon intersection preserving all disconnected components; returns GeoJSONPolygon[] | | boundingBox(polygon) | Compute BBox (minLon, minLat, maxLon, maxLat) | | centroid(polygon) | Geometric centre as { lat, lon } | | polygonArea(polygon) | Area in square metres | | circleToPolygon(centre, radiusMetres, segments?) | Approximate a circle as a GeoJSON Polygon (default 64 segments) | | getDestinationPoint(start, distanceMetres, bearingDeg) | Haversine destination point from [lon, lat], distance, and bearing |

Engines

| Class | Import path | Constructor | |-------|-------------|-------------| | ValhallaEngine | rendezvous-kit/engines/valhalla | { baseUrl } | | OpenRouteServiceEngine | rendezvous-kit/engines/openrouteservice | { apiKey, baseUrl? } | | GraphHopperEngine | rendezvous-kit/engines/graphhopper | { baseUrl, apiKey? } | | OsrmEngine | rendezvous-kit/engines/osrm | { baseUrl } |

Venues (rendezvous-kit/venues)

| Function | Description | |----------|-------------| | searchVenues(polygon, venueTypes, overpassUrl?) | Search Overpass API within polygon bounding box |

Types

| Type | Shape | |------|-------| | LatLon | { lat, lon, label? } | | GeoJSONPolygon | Standard GeoJSON polygon geometry | | TransportMode | 'drive' \| 'cycle' \| 'walk' \| 'public_transit' | | FairnessStrategy | 'min_max' \| 'min_total' \| 'min_variance' | | VenueType | 'park' \| 'cafe' \| 'restaurant' \| 'service_station' \| 'library' \| 'pub' \| 'playground' \| 'community_centre' \| string | | RoutingEngine | Interface — computeIsochrone + computeRouteMatrix | | Isochrone | { origin, mode, timeMinutes, polygon } | | MatrixEntry | { originIndex, destinationIndex, durationMinutes, distanceKm } | | RouteMatrix | { origins, destinations, entries } | | Venue | { name, lat, lon, venueType, osmId? } | | RendezvousOptions | { participants, mode, maxTimeMinutes, venueTypes, fairness?, limit? } | | RendezvousSuggestion | { venue, travelTimes, fairnessScore } | | BBox | { minLon, minLat, maxLon, maxLat } | | Coordinate | { lat, lon } |

Subpath Exports

import { findRendezvous } from 'rendezvous-kit'                          // barrel
import { intersectPolygons, intersectPolygonsAll, centroid,
         circleToPolygon, getDestinationPoint } from 'rendezvous-kit/geo' // geometry
import { ValhallaEngine } from 'rendezvous-kit/engines/valhalla'
import { OpenRouteServiceEngine } from 'rendezvous-kit/engines/openrouteservice'
import { GraphHopperEngine } from 'rendezvous-kit/engines/graphhopper'
import { OsrmEngine } from 'rendezvous-kit/engines/osrm'
import { searchVenues } from 'rendezvous-kit/venues'
import { findRendezvous } from 'rendezvous-kit/rendezvous'               // same as barrel

Pipeline

findRendezvous runs six steps:

  1. Isochrones — compute a reachability polygon for each participant
  2. Intersection — intersect all polygons using Sutherland–Hodgman (supports concave shapes and disconnected components)
  3. Venue search — query Overpass API within the intersection's bounding box
  4. Route matrix — compute travel times from every participant to every candidate venue
  5. Filtering — remove venues where any participant exceeds the time budget or is unreachable
  6. Scoring — apply the fairness strategy to produce a single score per venue
  7. Ranking — sort by score ascending and return the top limit suggestions

If the isochrones do not overlap, findRendezvous returns an empty array. If no venues are found, it falls back to the geometric centroid of the intersection.

Implementing a Custom Engine

import type { RoutingEngine, LatLon, TransportMode, Isochrone, RouteMatrix } from 'rendezvous-kit'

class MyEngine implements RoutingEngine {
  readonly name = 'MyEngine'

  async computeIsochrone(origin: LatLon, mode: TransportMode, timeMinutes: number): Promise<Isochrone> {
    // call your API and return an Isochrone
  }

  async computeRouteMatrix(origins: LatLon[], destinations: LatLon[], mode: TransportMode): Promise<RouteMatrix> {
    // call your API and return a RouteMatrix
  }
}

Companion Library

geohash-kit — spatial primitives (pointInPolygon, GeoJSON types, distance utilities) used internally by rendezvous-kit.

For AI Assistants

See llms.txt for a concise API summary, or llms-full.txt for the complete reference with examples.

Troubleshooting

findRendezvous returns an empty array The participants' isochrones don't overlap — they are too far apart for the given maxTimeMinutes. Try increasing the time budget or switching to a faster transport mode.

No venues found (fallback to centroid) The Overpass API found no matching venues within the intersection zone. This can happen in rural areas. The library returns a synthetic "Meeting point" at the area-weighted centroid as a fallback. Try broader venueTypes or a larger maxTimeMinutes.

RangeError: findRendezvous requires at least 2 participants You must pass at least 2 participants. Single-origin use cases don't need a meeting-point library — query the routing engine directly.

Engine HTTP errors (e.g., Valhalla isochrone error: 400) Check that your engine base URL is correct and the service is running. For ORS, verify your API key is valid. For Valhalla/OSRM, ensure the server has routing tiles loaded for the region you're querying.

OSRM: Error: OSRM does not support isochrone computation OSRM cannot generate isochrones. Use Valhalla, OpenRouteService, or GraphHopper instead. OSRM is supported only for route matrix computation.

Licence

MIT

Support

For issues and feature requests, see GitHub Issues.

If you find rendezvous-kit useful, consider sending a tip:

  • Lightning: [email protected]
  • Nostr zaps: npub1mgvlrnf5hm9yf0n5mf9nqmvarhvxkc6remu5ec3vf8r0txqkuk7su0e7q2