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

@pickpoint/sdk

v2.0.10

Published

Pickpoint SDK for JavaScript/TypeScript (public API + tracking)

Downloads

1,006

Readme

@pickpoint/sdk

Official JavaScript/TypeScript SDK for Pickpoint — a geolocation platform with four APIs under one key:

| API | What it does | |-----|----------------| | Geocoding | Address ↔ coordinates (forward, reverse, place lookup) | | Address search | Typeahead / autocomplete for address inputs | | Routing | Routes, matrices, optimized multi-stop, elevation | | Device tracking | Register devices over HTTP; stream live GPS over WebSocket |

Built for maps, delivery, logistics, and anything that needs places, routes, or live location. Data is OpenStreetMap-backed; responses are plain JSON / GeoJSON. Docs: pickpoint.io/docs.

This package wraps that surface so you do not hand-roll fetch / WebSocket framing:

  • PickPoint — HTTP client for geocode, search, routing, devices (+ clientAuth for SPAs)
  • @pickpoint/sdk/tracking — live GPS over WebSocket (tracking.v2)

Apache-2.0. Go sibling: github.com/pickpoint/go-sdk.

npm i @pickpoint/sdk

Quick start — PickPoint

One client, one auth session, whole public HTTP API.

import { PickPoint } from '@pickpoint/sdk'

// Browser: pair from YOUR backend (see clientAuth below)
const pp = new PickPoint({
  clientAuth: {
    accessToken: pair.accessToken,
    refreshToken: pair.refreshToken,
    expiresAt: pair.expiresAt, // unix ms
  },
})

// Flat shortcuts (also safe to destructure — methods are bound)
await pp.forward({ q: 'Berlin' })
await pp.reverse({ lat: 52.5, lon: 13.4 })
await pp.search({ q: 'Berlin' })          // address autocomplete
await pp.route({ locations: [/* … */] }) // Valhalla body
await pp.devices.list()

const { forward, search } = pp
await forward({ q: 'Paris' })

// Namespaced (clearer in larger apps)
await pp.geocoding.lookup({ osm_ids: 'R62422' })
await pp.routing.matrix({ /* … */ })
await pp.devices.command(uid, new TextEncoder().encode('ping'))

pp.close()

Style tip: use pp.forward(...) day-to-day; prefer pp.devices.* / pp.routing.* for domains with generic verbs (list, get, route). Destructuring works because shortcuts are arrow class fields.

Tracking (live WebSocket) is separate: @pickpoint/sdk/tracking.

Security: never put a secret API key in the browser

| Environment | How to auth | |-------------|-------------| | Node / your backend | apiKey (x-api-key) OK | | Browser / embedded web app | clientAuth pair from your backend |

// Node
const pp = new PickPoint({ apiKey: process.env.PICKPOINT_API_KEY! })

clientAuth (integrator SPA)

// ——— Your backend, after session check ———
// POST https://api.pickpoint.io/v2/client-tokens
// Headers: x-api-key: <SECRET>
// Body: { "scopes": ["geocoding", "address", "routing", "devices"], "ttlSec": 600 }
//   or omit scopes → all client-tokenable permissions on the key
// → { accessToken, refreshToken, expiresAt, expiresIn, scopes }

const pair = await fetch('/api/pickpoint/client-tokens', { credentials: 'include' })
  .then((r) => r.json())

const pp = new PickPoint({
  clientAuth: {
    accessToken: pair.accessToken,
    refreshToken: pair.refreshToken,
    expiresAt: pair.expiresAt,
  },
})

| Scope on mint | SDK surface | |---------------|-------------| | geocoding | forward / reverse / lookup | | address | search | | routing | route / optimizedRoute / matrix / locate / elevation | | devices | devices.* | | — | /v2/api-keys* not allowed with client tokens |

SDK refreshes at ~50% of access TTL (single-flight) and once on HTTP 401.

Batch geocoding

Pass an array (max 20 in flight; keep-alive via undici on Node):

await pp.forward([{ q: 'Paris' }, { q: 'Rome' }])
await pp.geocoding.batch.reverse([{ lat: 48.85, lon: 2.35 }])

| Response | Geocode batch slot | |----------|--------------------| | 2xx | parsed JSON | | 400 / other 4xx (except auth) | empty ([] / null) | | 401 | refresh once; else abort | | 402 / 403 | abort, ApiAuthError | | ≥500 / network | retry then throw |

Address / routing / devices throw ApiError on 4xx (with status + body) instead of empty slots.

Tracking

Live GPS is a separate WebSocket session (@pickpoint/sdk/tracking): wss://tracking.pickpoint.io/v2/ws, subprotocol tracking.v2. Works in browsers (global WebSocket) and Node (ws).

A dropped socket is not a new trip. The SDK reconnects and Resumes the same track_uid.

First publish starts the trip if none is live. close sends TrackStop then hangs up. Call startTrack only to supersede (new order / TRACK_NOT_FOUND) or to set a route.

Device (publisher)

import { connect } from '@pickpoint/sdk/tracking'

const session = await connect({
  endpoint: 'wss://tracking.pickpoint.io', // host; SDK appends /v2/ws
  auth: { clientId: deviceUid, clientSecret: deviceSecret }, // from devices.create
})

session.publish({ latitude: 55.75, longitude: 37.61 }) // TrackStart if idle
session.close() // TrackStop + hang up

Listener (dashboard)

accessToken is the client-token from POST /v2/client-tokens (scope devices) — same pair.accessToken as HTTP clientAuth. Mint it on your backend; never put the API key in the browser.

const session = await connect({
  endpoint: 'wss://tracking.pickpoint.io',
  auth: { accessToken: pair.accessToken },
  subscribe: deviceUid,
})

session.on('location', (msg) => {
  console.log(msg.point.latitude, msg.point.longitude) // live fan-out; publisher never sees Loc
})

Wire format: pickpoint-proto.

Develop

npm i
npm test
npm run build

Live geocode batch e2e (suite skipped unless the key is set):

PICKPOINT_API_KEY=… npm test -- test/e2e-geocode-batch.test.ts
# optional: PICKPOINT_BASE_URL=https://api.pickpoint.io

CI & release

  • PR.github/workflows/ci.yml (typecheck, test, build on Node 20/22)
  • Push to main (untagged HEAD) → bump patch, tag vX.Y.Z, npm publish (OIDC) + GitHub Release in the same job
    (tag push via GITHUB_TOKEN does not start new workflows — publish cannot wait on the tag event)
  • Manual tag v* (pushed by a human) → publish + GitHub Release

Minor/major: bump version in a PR, merge with [skip release] in the commit message, then:

git tag v2.2.0
git push origin v2.2.0