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

@visamundi/visamundi-vision

v0.1.0

Published

The read-only Gmail email-reading engine behind Visamundi Vision: scans an inbox for travel bookings (flights, hotels, trains, car rentals) and extracts structured trip data. Bring your own LLM.

Readme

👁️ Visamundi Vision

The email-reading engine behind Visamundi Vision — open-sourced so you can audit exactly what we read.

npm License: MIT Gmail scope

Read-only. No writing, no deleting, no storage. Bring your own LLM.


Why this repo exists

Visamundi Vision scans your inbox to find flights, hotels, trains and car rentals, then tells you which visas and travel formalities you need. That requires access to your email, and access to your email demands trust.

So we don't ask you to trust us. This repository is the actual code that reads and interprets your emails — the same package our production app installs from npm. You can read every line, run it yourself, and confirm three promises:

  1. We only ever read. The single Gmail scope requested is gmail.readonly. There is no code path that sends, replies, labels, archives, or deletes anything.
  2. We don't keep your email. This library extracts a handful of trip fields (dates, destination, booking reference) and returns them to the caller. It writes nothing to disk and phones no home server.
  3. The AI backend is yours to choose. The trip extractor talks to any OpenAI-compatible model you configure. There is no hidden third party baked in.

What stays closed is everything that has nothing to do with reading your mailbox: visa rules, the dashboard, accounts, billing, our database.

What's in / what's out

| In this package (auditable) | Kept in the private app | | --- | --- | | Gmail search + fetch (gmail.readonly) | OAuth login & token storage | | Candidate triage & heuristic pre-filter | User accounts, database, RLS | | LLM trip extraction (provider you supply) | Visa & formality rules | | Confidence scoring, trip stitching | Scan scheduling / orchestration | | Airport / IATA / city normalization | Web dashboard & emails |

Install

npm install @visamundi/visamundi-vision
# or: pnpm add @visamundi/visamundi-vision

Quick start

Give it a Gmail access token, get structured trips back. Token acquisition (OAuth) is your responsibility — this library never sees your client secret.

import { scanEmailsForTrips, GMAIL_SCOPES } from "@visamundi/visamundi-vision"
import { createOpenAICompatible } from "@ai-sdk/openai-compatible"

// 1. The ONLY scope this library needs:
console.log(GMAIL_SCOPES) // ["https://www.googleapis.com/auth/gmail.readonly"]

// 2. Bring your own OpenAI-compatible model (OpenAI, Mistral, a local model…)
const provider = createOpenAICompatible({
  name: "my-llm",
  apiKey: process.env.MY_LLM_API_KEY!,
  baseURL: "https://api.openai.com/v1",
})

// 3. Scan. Omit `llm` entirely to run the regex-only fallback with no AI at all.
const trips = await scanEmailsForTrips(accessToken, {
  lastScanDate: null,
  llm: { provider, extractorModel: "gpt-4o-mini" },
})

console.log(trips)
// [{ bookingType: "flight", destination: "Tokyo, Japan", startDate: "2026-09-12", confirmationNumber: "ABC123", ... }]

API

scanEmailsForTrips(accessToken, options?)

| Param | Type | Description | | --- | --- | --- | | accessToken | string | A Google OAuth access token carrying the gmail.readonly scope. | | options.lastScanDate | Date \| null | Only look at mail newer than this. null = default window (last 3 months). | | options.llm | { provider, classifierModel?, extractorModel? } | OpenAI-compatible provider + model ids. Omit to disable AI and use the deterministic regex extractor. | | options.getProcessedEmailIds | (ids: string[]) => Promise<Set<string>> | Optional dedup hook: return the subset of message IDs you've already handled so they're skipped. Default: process everything. |

Returns Promise<DetectedTripData[]>.

Also exported: GMAIL_SCOPES, stitchTrips, normalizeDestination, computeHeuristicScore, computeFinalScore, the lower-level Gmail helpers, and all pipeline types.

How it works

Gmail (read-only)
   │  messages.list  ── date-bounded, promotions/social/forums excluded
   ▼
Candidate metadata (sender · subject · snippet)
   │  triage          ── LLM (or known-sender shortcut) keeps likely bookings
   ▼
Heuristic pre-filter  ── PNR/IATA/flight-number/date regex scoring
   │  messages.get    ── full body fetched ONLY for retained candidates
   ▼
LLM extraction        ── your model pulls structured trip fields
   │
   ▼
Score → stitch → normalize destination → DetectedTripData[]

Every network call to Gmail is a read (messages.list, messages.get). Grep the source — you will not find messages.send, .modify, .trash, or .delete.

Extracted fields

Per detected trip: booking type (flight / hotel / train / car), destination (normalized to city + IATA), start/end dates, confirmation number, carrier/provider, and an extraction-confidence score. Nothing else is read or retained.

Audit it yourself

  1. Confirm the scope: GMAIL_SCOPES in src/constants.ts — read-only, full stop.
  2. Confirm no writes: search the repo for send|modify|trash|delete against the Gmail client. Only list/get exist.
  3. Confirm no exfiltration: the only outbound calls are to Google (read) and to the LLM provider you configured.
  4. Run the demo in example/ against your own account.

Relationship to the product

This package is published from this repo and consumed as a pinned dependency by the closed-source Visamundi Vision app. The version our app runs is the version you see here — verifiable via the lockfile and the npm registry.

License

MIT © Visamundi