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

@geoseal/react-views

v0.6.0

Published

Embeddable React components for Geoseal Views — visit proof, shift report, live location, fleet map, route replay, signal integrity, and the write-capable place editor, availability editor, and approvals queue — each rendered from a single signed Geoseal

Readme

@geoseal/react-views

npm license

Drop-in React components for the nine Geoseal Views:

  • <GeosealVisit> — a natively-rendered visit proof view: Mapbox map with the facility's two geofence rings, the worker's location track, a verified-presence event timeline, and the AI closeout summary.
  • <GeosealShiftReport> — the hosted per-shift proof page (route replay, KPIs, tracked-% bar, four-source timesheets) in a sandboxed iframe.
  • <GeosealLive> — the hosted live location page for a shift (pulsing current-position marker inside the geofence) in a sandboxed iframe.
  • <GeosealFleet> — the hosted app-wide live map: every tracked subject as a pin, the watched places and their rings, and on-site / outside / offline counts.
  • <GeosealReplay> — the hosted route playback for one shift: the trail as a polyline with play / pause / speed / scrub and an event timeline.
  • <GeosealIntegrity> — the hosted signal-quality table: tracking health per subject, worst-first, with severity chips (no map).
  • <GeosealPlaceEditor> — the hosted, write-capable geofence editor for a place (draggable center, radius rings, polygon fences, hours & closures) in a sandboxed iframe. Saves land in Geoseal with full audit.
  • <GeosealAvailability> — the hosted, write-capable availability editor for one subject: the weekly local-time windows they can work, plus dated exceptions. A mirror of your scheduler, never a gate on a shift.
  • <GeosealApprovals> — the hosted, write-capable approvals queue: pending timesheet proposals and open dispute threads, actioned through the same RPCs the Geoseal console uses.

All nine render from a single signed, time-boxed, de-identified embed token (etok_…) that you mint server-side (see Mint a token). The six view components need settings.embed_views_enabled = true on your App. Each write component has its own switch instead — embed_editor_enabled, embed_availability_editor_enabled (which also needs the availability_enabled domain flag), and embed_approvals_enabled — so turning on one never turns on another. Everything is off by default.

New — v0.6.0. Two write-capable views: <GeosealAvailability> (one subject's weekly availability + dated exceptions) and <GeosealApprovals> (the pending timesheet-proposal / dispute queue), plus the "availability_editor" and "approvals" kinds on buildEmbedUrl(). Both mint with the editor posture (actor + allowed_origins required, 900s default TTL / 3600s max) and each has its own feature flag. onSaved now receives an EmbedSavedDetail naming what was written ({ resource, id }) — an existing onSaved={() => …} handler is unaffected.

v0.5.0. Appearance v2: appearance.brand (logo, corner-radius scale, font stack) and appearance.locale (en / es), serialized as the radius / font / locale embed params. Views also inherit the brand preset saved on your App, so you can brand once instead of per-iframe — anything you pass here still wins for that embed. The logo is the exception: it has no URL param, so the hosted views take it from the App preset only.

v0.4.0. Three read views: <GeosealFleet> (app-wide live map), <GeosealReplay> (per-shift route playback), <GeosealIntegrity> (signal quality table) — and buildEmbedUrl() gains the "fleet", "replay", and "integrity" kinds. Fleet tokens are App-scoped, so their mint requires allowed_origins and is capped at a 3600s TTL.

v0.3.0. <GeosealPlaceEditor> — the first write-capable embed — with onSaved / onDirtyChange host events, and the "place_editor" kind.

v0.2.0. Appearance API (appearance prop: theme / accent / basemap / hide) on every component, <GeosealLive>, auto-height iframes, host events (onReady / onError / onStateChange), and buildEmbedUrl() for hand-rolled iframes. The theme prop on <GeosealVisit> is deprecated in favor of appearance.theme.

Published versions: as of 2026-08-04 (UTC) the npm latest tag is 0.5.0 (npm view @geoseal/react-views version; published: 0.1.0, 0.1.1, 0.2.0, 0.3.0, 0.4.0, 0.5.0) — 0.6.0 is this source tree and is not on npm yet, so npm install @geoseal/react-views still resolves to the Appearance-v2 release, without the availability and approvals components.

Install

npm install @geoseal/react-views mapbox-gl
# react and react-dom (>=18) are peer dependencies you already have

mapbox-gl is a peer dependency — <GeosealVisit> uses the publishable Mapbox token returned inside the view payload, so you do not configure Mapbox yourself. The iframe components don't touch it at all.

Usage

import {
  GeosealVisit,
  GeosealShiftReport,
  GeosealLive,
  GeosealFleet,
  GeosealReplay,
  GeosealIntegrity,
  GeosealPlaceEditor,
  GeosealAvailability,
  GeosealApprovals,
} from "@geoseal/react-views";

// Visit proof (native render)
<GeosealVisit token={etok} live appearance={{ theme: "auto", accent: "6d28d9" }} />

// Shift report (hosted iframe, auto-sizes to content)
<GeosealShiftReport token={etok} appearance={{ theme: "light" }} />

// Live location (hosted iframe; token minted with kind "live")
<GeosealLive
  token={etok}
  appearance={{ theme: "dark", accent: "6d28d9", basemap: "satellite" }}
  onStateChange={(live) => setBadge(live ? "On shift" : "Ended")}
/>

// Fleet map (hosted iframe, app-wide; token minted with kind "fleet")
<GeosealFleet token={etok} appearance={{ theme: "dark", basemap: "dark" }} />

// Route replay for a shift (hosted iframe; token minted with kind "replay")
<GeosealReplay token={etok} onStateChange={(live) => setFollowing(live)} />

// Signal integrity table (hosted iframe, app-wide; token minted with kind "integrity")
<GeosealIntegrity token={etok} appearance={{ theme: "light" }} />

// Branded + localized (any component): logo, radius scale, font, language
<GeosealShiftReport
  token={etok}
  appearance={{
    accent: "6d28d9",
    brand: { logoUrl: "https://cdn.example.com/logo.svg", radius: "round" },
    locale: "es",
  }}
/>

// Place editor (hosted iframe, WRITE-capable; token minted with kind "place_editor")
<GeosealPlaceEditor
  token={etok}
  appearance={{ theme: "auto" }}
  onDirtyChange={(dirty) => setUnsaved(dirty)}
  onSaved={() => toast("Geofence updated")}
/>

// Availability for one subject (hosted iframe, WRITE-capable;
// token minted with kind "availability_editor")
<GeosealAvailability
  token={etok}
  onDirtyChange={(dirty) => setUnsaved(dirty)}
  onSaved={() => refetchAvailability()}
/>

// Approvals queue (hosted iframe, app-wide, WRITE-capable;
// token minted with kind "approvals")
<GeosealApprovals
  token={etok}
  onSaved={({ resource }) => refetchQueue(resource)}
/>

That is the whole integration: pass the etok_… token, get a view.

The appearance prop (all components)

| Field | Type | Default | Notes | | --------- | ------------------------------------------------- | ---------- | --------------------------------------------------------------------- | | theme | 'light' \| 'dark' \| 'auto' | per view | auto follows prefers-color-scheme. Visit defaults dark, shift report light; every other view defaults to auto. | | accent | string | Geoseal teal | 6-digit hex, # optional (e.g. "6d28d9"). Colors buttons, chips, KPI highlights, the track line, and the live marker. Invalid values are ignored. | | basemap | 'auto' \| 'streets' \| 'dark' \| 'satellite' | 'auto' | auto matches the resolved theme. | | hide | string[] | [] | Sections to hide, per view: visit — header, timeline, closeout; shift report — header, kpis, timesheets, activity; fleet — header, counts; replay — header, timeline, playback; integrity — header, footnote; approvals — header, counts; live / place editor / availability — header. Unknown values are ignored; the "Powered by GeoSeal" footer is not hideable. | | brand | GeosealBrand | — | Logo / radius / font tokens (see below). | | locale | 'en' \| 'es' | 'en' | Language for the view's own strings and its date/time formatting. A string without a translation falls back to English, never a lookup key. Unset, <GeosealVisit> renders English strings and keeps the viewer's own runtime time format. |

brand fields, all optional:

| Field | Type | Default | Notes | | --------- | ------------------------------------- | ---------- | --------------------------------------------------------------------------- | | logoUrl | string | — | <GeosealVisit> only (it renders in your own page). Shown in the view header at most 120x32 (object-fit: contain); must be https: and ≤512 chars — data: and plain-http URLs are dropped. The hosted iframe views take a logo from your App's brand preset instead: it is the one appearance field with no URL param, so holding an embed link can never change whose mark a GeoSeal page shows. The "Powered by GeoSeal" attribution stays either way. | | radius | 'sharp' \| 'soft' \| 'round' | 'soft' | Corner-radius scale for cards, chips, and buttons. soft is the stock geometry. | | font | 'system' \| 'mono' | 'system' | System stacks only — the hosted pages fetch no webfont (strict CSP). |

On the iframe components the appearance is serialized onto the embed URL; on <GeosealVisit> it drives the native renderer directly.

Precedence. A field you set here always wins, then the brand preset saved on your App (set once in the GeoSeal console), then the built-in default. So an App-level preset re-brands every embed without touching a single iframe URL, and an existing URL keeps rendering exactly as it did before the preset existed. The hosted pages apply the preset themselves; <GeosealVisit> reads it off the payload (EmbedView.appearance, {} when unset) and merges it the same way — so custom renderers built on useEmbedView can honor it too.

<GeosealVisit> props

| Prop | Type | Default | Notes | | --------------------- | ----------------------- | ------------------- | ------------------------------------------------------------ | | token | string | — | Required. The etok_… Geoseal Views token. | | baseUrl | string | live functions host | Override to target a different Geoseal deployment. | | live | boolean | false | Poll for updates while the visit is open. | | pollIntervalSeconds | number | 15 | Poll cadence when live. Stops automatically once closed. | | height | number \| string | 520 | Overall height of the view. | | appearance | GeosealAppearance | — | Theme / accent / basemap / hide / brand / locale (see above). | | theme | 'dark' \| 'light' | 'dark' | Deprecated — use appearance.theme; it wins when set. This prop still outranks the App preset, so saving one never re-themes an integration that pinned a theme here. | | className / style | — | — | Applied to the root element. |

Hosted read-view props

<GeosealShiftReport>, <GeosealLive>, <GeosealFleet>, <GeosealReplay>, and <GeosealIntegrity> take the same iframe props:

| Prop | Type | Default | Notes | | --------------------- | --------------------------- | ---------------------- | ---------------------------------------------------------------------- | | token | string | — | Required. Minted with the component's kind (see below). | | embedOrigin | string | https://geoseal.dev | Hosted embed origin. | | appearance | GeosealAppearance | — | Serialized onto the embed URL. | | autoHeight | boolean | true | Follow the page's resize messages. An explicit height disables it. | | height | number \| string | auto (per-view start) | Fixed height; opts out of autoHeight. | | onReady | () => void | — | The page rendered its payload. | | onError | (code: string) => void | — | e.g. expired_embed_token, revoked_embed_token, origin_not_allowed. | | onStateChange | (live: boolean) => void | — | Live-ness changed (shift started / ended, visit closed). Shift report, live, and replay only — fleet and integrity have no single live-ness. | | title | string | per component | iframe title for assistive tech. | | className / style | — | — | Applied to the iframe. |

| Component | Mint kind | Scope | Starting height | Refresh | | --------------------- | -------------- | -------------- | --------------- | ---------- | | <GeosealShiftReport>| shift_report | one shift | 720 | on load | | <GeosealLive> | live | one shift | 480 | ~5s while live (60s once it ends) | | <GeosealFleet> | fleet | the whole App | 560 | ~10s | | <GeosealReplay> | replay | one shift | 640 | ~15s while live | | <GeosealIntegrity> | integrity | the whole App | 480 | ~60s |

<GeosealFleet> and <GeosealIntegrity> are App-scoped — one token covers everyone currently tracked, not a single shift. Fleet is the widest surface in the set, so its mint is held to the editor's posture: allowed_origins is required and the TTL defaults to 900s (max 3600s). Pass ops_group at mint time to narrow the map to one operational group. Both cap and de-identify what they return — fleet serves at most 200 subjects (truncated: true when it clips) and 50 places, and no App or external IDs cross the wire.

<GeosealReplay> serves the same trail as the shift report, clamped to the shift window ± 45 min and capped at 2000 points.

<GeosealPlaceEditor> props

Same iframe props as above (token, embedOrigin, appearance, autoHeight, height — starts at 640, onReady, onError, title, className / style), minus onStateChange (an editor has no live-ness), plus the write callbacks:

| Prop | Type | Notes | | --------------- | ----------------------------------- | ------------------------------------------------------------------ | | onSaved | (detail: EmbedSavedDetail) => void| A save round-tripped and persisted (refresh your own UI, webhooks already fired). detail is { resource?, id? }resource: "place", plus id on a create (the only write that names a new row; an edit sends resource alone). | | onDirtyChange | (dirty: boolean) => void | Unsaved edits appeared (true) or were saved/discarded (false) — e.g. warn before navigating away. |

onError additionally surfaces embed_editor_disabled when the Place Editor flag is off for the App.

A place_editor token minted without place_id opens the editor in create mode — map-drop or address search, name, rings — and onSaved fires with detail.id set to the place it created. Create and edit tokens are not interchangeable: the mode is fixed in the token's claims at mint time.

The callbacks are driven by the hosted pages' postMessage envelope ({ source: "geoseal", version: 1, type, … }), which carries no view or place data — only lifecycle signals (ready, resize, error, state, saved, dirty).

<GeosealAvailability> and <GeosealApprovals> props

Both take the same iframe props as <GeosealPlaceEditor> (token, embedOrigin, appearance, autoHeight, height, onReady, onError, title, className / style). What differs is the kind they are minted with, what that kind covers, and which write callbacks fire:

| Component | Mint kind | Scope | Starting height | Write callbacks | | ----------------------- | ---------------------- | --------------- | --------------- | --------------------------- | | <GeosealAvailability> | availability_editor | one subject | 640 | onSaved, onDirtyChange | | <GeosealApprovals> | approvals | the whole App | 720 | onSaved only |

<GeosealApprovals> has no onDirtyChange: a queue has no draft state — each approve / dismiss / resolve is explicit and immediate, and fires onSaved with the resource that moved ({ resource: "proposal" | "thread" } — the same two words the queue's items are discriminated by).

onError surfaces embed_availability_editor_disabled and embed_approvals_disabled respectively when the kind's flag is off for the App — each is a separate switch from embed_views_enabled and from the Place Editor's, and the availability editor additionally needs the availability_enabled domain flag.

What an availability token can reach. Exactly one subject's own windows: the de-identified handle, the resolved timezone, the weekly rules, and the dated exceptions. No positions, no shifts, no other subject — and availability is a mirror, so nothing you save here can block a shift, a crossing, or a payroll decision. It only feeds an additive warning signal.

What an approvals token can do. Take payroll-affecting actions for the whole App. Every action is dispatched to the same RPC the Geoseal console calls and is audited as embed:<actor>, so it is reviewable next to console actions — but treat the link like the console session it stands in for: scope allowed_origins tightly and keep the TTL short.

The useEmbedView hook

Render it yourself if you don't want the built-in UI:

import { useEmbedView } from "@geoseal/react-views";

const { data, loading, error, refetch } = useEmbedView(etok, { live: true });
// data is a fully-typed EmbedView (or null); error.kind tells you why (expired,
// not_found, network, server).

useShiftReport does the same for the shift-report payload.

The iframe alternative

If you don't want a React dependency at all, embed the hosted page directly — buildEmbedUrl builds the exact URL the components use, appearance included:

import { buildEmbedUrl } from "@geoseal/react-views";

buildEmbedUrl("live", "etok_…", { appearance: { theme: "dark", accent: "6d28d9" } });
// -> "https://geoseal.dev/embed/live/etok_…?theme=dark&accent=6d28d9"

buildEmbedUrl("fleet", "etok_…");
// -> "https://geoseal.dev/embed/fleet/etok_…"

buildEmbedUrl("shift_report", "etok_…", {
  appearance: { brand: { radius: "round", font: "mono" }, locale: "es" },
});
// -> "…/embed/shift-report/etok_…?radius=round&font=mono&locale=es"

buildEmbedUrl("availability_editor", "etok_…", { appearance: { locale: "es" } });
// -> "https://geoseal.dev/embed/availability/etok_…?locale=es"

Appearance params, in the order they are serialized: theme, accent, basemap, hide, radius, font, locale. brand.logoUrl is not among them — the hosted pages take a logo from the App preset only — and any param you leave out falls through to that preset.

Kinds: "visit", "shift_report", "live", "fleet", "replay", "integrity", "place_editor", "availability_editor" (page: /embed/availability/…), "approvals" — each maps to its hosted page under /embed/…. The kind must match the kind the token was minted with; a mismatch fails closed with wrong_embed_kind.

<iframe
  src="https://geoseal.dev/embed/visit/etok_…?theme=dark"
  width="100%"
  height="520"
  style="border:0;border-radius:12px"
  loading="lazy"
  referrerpolicy="strict-origin-when-cross-origin"
  title="Geoseal — live shift"
></iframe>

Mint a token

A token authorizes exactly one visit, shift, place, or subject — or, for the App-scoped fleet, integrity, and approvals kinds, one slice of your own App — is time-boxed and revocable, and carries no other account access. Mint it server-side with your secret key (sk_, never in the browser):

# Visit proof
curl -X POST https://api.geoseal.dev/functions/v1/v1-embed/embed/tokens \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "visit_id": "vis_…", "ttl_seconds": 3600 }'

# Live location for a shift
curl -X POST https://api.geoseal.dev/functions/v1/v1-embed/embed/tokens \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "shift_ref": "SI-1234", "kind": "live", "ttl_seconds": 14400 }'
# -> { "token": "etok_…", "embed_url": "https://geoseal.dev/embed/live/etok_…", "expires_at": "…" }

# Route replay for a shift
curl -X POST https://api.geoseal.dev/functions/v1/v1-embed/embed/tokens \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "shift_ref": "SI-1234", "kind": "replay", "ttl_seconds": 3600 }'

# Fleet map (App-scoped, so stricter): "allowed_origins" is REQUIRED and the
# TTL defaults to 900s, max 3600s. "ops_group" is optional and narrows the map.
curl -X POST https://api.geoseal.dev/functions/v1/v1-embed/embed/tokens \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "kind": "fleet", "allowed_origins": ["https://app.example.com"],
        "ops_group": "north-region", "ttl_seconds": 900 }'

# Signal integrity table (App-scoped, normal read TTL)
curl -X POST https://api.geoseal.dev/functions/v1/v1-embed/embed/tokens \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "kind": "integrity", "ttl_seconds": 3600 }'

# Place editor (write-capable, so stricter): "actor" — your user's reference,
# recorded on every audit entry — and "allowed_origins" are REQUIRED.
# TTL defaults to 900s, max 3600s.
curl -X POST https://api.geoseal.dev/functions/v1/v1-embed/embed/tokens \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "place_id": "place_…", "kind": "place_editor", "actor": "jane@yourapp",
        "allowed_origins": ["https://app.example.com"], "ttl_seconds": 900 }'

# Place editor in CREATE mode: the same kind with NO "place_id".
curl -X POST https://api.geoseal.dev/functions/v1/v1-embed/embed/tokens \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "kind": "place_editor", "actor": "jane@yourapp",
        "allowed_origins": ["https://app.example.com"] }'

# Availability editor (write-capable, scoped to ONE subject): same required
# "actor" + "allowed_origins", same 900s/3600s TTL.
curl -X POST https://api.geoseal.dev/functions/v1/v1-embed/embed/tokens \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "subject_id": "sub_…", "kind": "availability_editor", "actor": "jane@yourapp",
        "allowed_origins": ["https://app.example.com"], "ttl_seconds": 900 }'

# Approvals queue (write-capable, App-scoped — its actions move payroll, so
# keep the origin list tight and the TTL short).
curl -X POST https://api.geoseal.dev/functions/v1/v1-embed/embed/tokens \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "kind": "approvals", "actor": "jane@yourapp",
        "allowed_origins": ["https://app.example.com"], "ttl_seconds": 900 }'

Hand the returned token to the matching component. To kill a live link, revoke the token server-side (DELETE /v1/embed/tokens/{jti}) and the next load fails closed. You can also pin a view token to your own site at mint time with allowed_origins — other embedders get origin_not_allowed; on fleet and the three write kinds it is not optional. Every write is audited exactly like the console edit it stands in for, attributed embed:<actor>.

Full reference: docs/embed-views.md.

What <GeosealVisit> renders

The component fetches GET {baseUrl}/v1-embed/embed/visits/{token} (public, no auth) and shows:

  • Map — the facility with its facility_radius_m (inner) and perimeter_radius_m (outer) geofence rings, the worker's track as a line, and a marker at the latest fix (pulsing while live).
  • Timeline — arrival → dwell → departure using the Geoseal presence colors, with any tracking outages folded in.
  • AI closeout — the closeout summary card when present. When the tenant has not opted into names, the narrative is withheld and only the structured confidence is shown (closeout.redacted).

Graceful loading, error, and expired / revoked states are built in.

License

Apache-2.0