# ExplorationMaps

**Category:** 💻 Developer Tools  
**Repository:** https://github.com/coltongriffith/mapviewer2  
**Views:** 0  
**Installs:** 0  
**Upvotes:** 0  
**Directory Page:** https://allmcps.com/mcp/explorationmaps

## Description
Mining claim search and professional mineral exploration maps from public registries.

## Claude Desktop Quick Installation
Heuristic fallback — verify the package name and runner against the repository README before running it. Uses `npx` (confidence: low):

```json
"mcpServers": {
  "explorationmaps": {
    "command": "npx",
    "args": ["-y","explorationmaps"]
  }
}
```

## Documentation & README

# Exploration Maps

Browser-based map builder for mineral exploration: search provincial claim
registries, import drill/geology data, style investor-ready maps, and export
them as PNG/SVG/PDF. Live at [explorationmaps.com](https://www.explorationmaps.com).

## Stack

- **Frontend:** React 18 + Vite 5 single-page app (`src/`), Leaflet map engine
- **Backend:** Supabase (auth, Postgres, RLS) + Vercel serverless functions (`api/`)
- **Static marketing content:** generated blog + company pages under `public/`
  (see `scripts/generate-blog.js` and `scripts/pseo/`)

## Requirements

- Node 20+ (Vite 5 requirement; dev/CI verified on Node 20/22)
- npm (a `package-lock.json` is committed — use `npm ci` for reproducible installs)

## Development

```bash
npm ci                 # install exactly the locked dependencies
cp .env.example .env   # fill in the VITE_ variables (see below)
npm run dev            # Vite dev server on http://localhost:5173
```

The serverless functions under `api/` run on Vercel. For full-stack local
work use `vercel dev`; with plain `npm run dev` the claims search and
analytics endpoints are absent (the UI degrades gracefully).

## Commands

| Command                | What it does                                              |
| ---------------------- | --------------------------------------------------------- |
| `npm run dev`          | Vite dev server                                           |
| `npm run build`        | Generates the blog, builds to `dist/`, copies the admin shell |
| `npm test`             | Vitest run (CI mode)                                      |
| `npm run test:watch`   | Vitest watch mode                                         |
| `npm run pseo:fixture` | pSEO company-pages pipeline against fixture data          |

## Environment variables

Documented in `.env.example`. Summary:

**Public (`VITE_` — compiled into the client bundle, never secret):**

- `VITE_SUPABASE_URL`, `VITE_SUPABASE_ANON_KEY` — Supabase project + anon key
  (production-required; without them auth/cloud features disable themselves)
- `VITE_ADMIN_EMAIL` — controls admin dashboard **visibility only**; real
  authorization is server-side (`admin_users` table + `is_admin()`)

**Server-only secrets (set in Vercel, never referenced by client code):**

- `SUPABASE_SERVICE_ROLE_KEY` — used by `/api/track` for analytics ingestion.
  Production-required once migration `20260710000004` is applied.
- `ADMIN_API_SECRET` — optional; unlocks the claims-API diagnostic modes in
  production via the `x-admin-secret` header.
- `SUPABASE_URL` / `SUPABASE_ANON_KEY` — optional server-side aliases; the
  `VITE_`-prefixed values are used as fallbacks by `api/claims.js` (Quebec)
  and `api/track.js`.
- `STRIPE_SECRET_KEY`, `STRIPE_WEBHOOK_SECRET`, `STRIPE_PRICE_MONTHLY_ID`,
  `STRIPE_PRICE_YEARLY_ID` — Pro-subscription billing (see `docs/billing.md`).
  Until set, billing endpoints return 503 and all plan checks fail open —
  safe to deploy the code before configuring Stripe.

## Architecture notes

### Tenure Monitor (`/tenure-monitor`)

Watches a saved portfolio of B.C. mineral tenures: days remaining against each
claim's good-to-date, email reminders, change detection, and one-click hand-off
into the map editor. Read-only with respect to government transactions — it
never touches MTO on a user's behalf.

- **Data**: a mirror of DataBC's `MTA_ACQUIRED_TENURE_SVW` layer, loaded by
  `scripts/tenure-sync/` from GitHub Actions (nightly full + twice-daily
  targeted). A partial or failed government response **aborts without writing**,
  so a bad day at the province cannot damage stored portfolios.
- **Reminders**: `scripts/tenure-alerts/`, daily. De-duplication is a unique
  database constraint rather than application logic.
- **Deadlines** are computed in `America/Vancouver` throughout
  (`src/utils/tenureDates.js`), never in the browser's timezone.
- Developer docs: [`docs/tenure-monitor.md`](https://github.com/coltongriffith/mapviewer2/blob/HEAD/docs/tenure-monitor.md).
  User help: [`docs/tenure-monitor-help.md`](https://github.com/coltongriffith/mapviewer2/blob/HEAD/docs/tenure-monitor-help.md).

### Serverless APIs (`api/`)

- `api/claims.js` — multi-province claims search proxy (BC WFS, ArcGIS
  provinces ON/SK/MB/NL/YT, Quebec via a self-hosted Supabase table). Full
  pagination with honest `meta: { totalKnown, returned, truncated,
  pagesFetched, provider }`; rate-limited; CORS-restricted; sanitized errors.
- `api/bc-claims.js` — BC-only WFS proxy (nearby-claims bbox + legacy search).
- `api/tenure-search.js` — Tenure Monitor search over the Exploration Maps
  mirror of the B.C. mineral tenure registry (tenure number, pasted list, owner
  name, client number, map extent, geometry). Anonymous-safe, rate-limited, and
  every response states when the mirror was last synchronized. Reads with the
  ANON key under the public-read policy, never the service role.
- `api/track.js` — all analytics ingestion (page views, live-presence pings,
  product/search events, landing clicks, leads). Enforces an event-name
  allowlist, payload size/depth limits, and session-id shape; derives geo
  from edge headers (never the body); resolves user identity from a verified
  Supabase access token; writes with the service role; rate-limits per IP.
  Without `SUPABASE_SERVICE_ROLE_KEY` it accepts-and-drops so analytics can
  never break the app.
- `api/stripe-checkout.js` / `api/stripe-portal.js` / `api/stripe-webhook.js`
  — Pro-subscription billing (Stripe Checkout + customer portal + the
  signature-verified webhook that syncs `public.user_plans`). Grandfathered
  accounts (pre-launch) are never downgraded. See `docs/billing.md`.
- `api/_lib/` — shared helpers (pagination, request guards, esri→GeoJSON);
  the underscore path means Vercel does not expose them as endpoints.

### Shared maps

Share links are `/map/<id>` where `<id>` is a client-generated
`crypto.randomUUID()` (a 122-bit random token). Reads go through the
`get_shared_map(share_id)` RPC — table-level SELECT is revoked, so
`shared_maps` cannot be enumerated from the public client. Old links keep
working: the id remains the lookup key.

### Authentication & admin

Supabase auth (magic-link default, password fallback). Admin access is a row
in `public.admin_users` checked by `is_admin()` inside SECURITY DEFINER
reporting functions; all definer functions have a pinned `search_path` and
explicit execute grants. To add an admin:

```sql
insert into public.admin_users (user_id, note)
select id, 'added by <you>' from auth.users where email = '<email>';
```

### Local project storage

`src/utils/projectStorage.js`: projects + the working draft live in
localStorage, deflate-compressed (`gz1:` prefix), with a schema version and
deterministic migrations. Corrupted records are preserved under a
`.recovery` key (never auto-deleted) and surfaced with manual export/discard
helpers. Writes return structured results — the UI never claims success on a
failed write. On sign-in, local projects migrate to the cloud with
per-project status + retry (`src/utils/cloudMigration.js`).

### Import formats

CSV (RFC 4180 via Papa Parse; auto-detected lat/long columns, plus azimuth,
dip and length for drill traces), GeoJSON (validated), zipped shapefiles,
loose `.shp/.dbf/.prj/.shx` sets (the `.prj` is honored — projected files are
reprojected to WGS84 via proj4), KML, KMZ (bounded: entry count, uncompressed
size, path traversal), and georeferenced images (`.png/.jpg/.gif/.webp` with
a world file or typed-in edges, lat/long or UTM; resampled to 2048 px and
stored in the project). Published tile (`{z}/{x}/{y}`) and WMS services can
be added as layers under Reference Overlays.

### Claim registries

Canada: BC (WFS), Ontario/Saskatchewan/Manitoba/Newfoundland &
Labrador/Yukon (official ArcGIS services), Quebec (weekly-synced Supabase
mirror). United States: federal BLM MLRS claims for 11 western states,
behind `VITE_ENABLE_US_CLAIMS` — federal claims only, no state-managed
tenure (Alaska state claims not included); boundaries are generalized BLM
representations, not legal surveys. See `docs/us-claims.md` for the data
source, field mapping, and the post-deploy verification checklist.

## Database migrations

Timestamped, additive migrations live in `supabase/migrations/`. The legacy
`supabase-*.sql` files in the repo root are the historical hand-run setup
scripts — they document existing production state; new changes go in
`supabase/migrations/` only.

Apply order matters — two migrations are deploy-coupled:

| Step | Action | When |
|------|--------|------|
| 1 | `20260710000001_shared_map_lookup_rpc.sql` | Any time (additive) |
| 2 | `20260710000003_live_pings_read_lockdown.sql` | Any time (the deployed app never reads live_pings) |
| 3 | `20260710000005_admin_authorization.sql` | Any time (additive; seeds the current admin) |
| 4 | Deploy the frontend (uses `get_shared_map` + `/api/track`) and set `SUPABASE_SERVICE_ROLE_KEY` in Vercel | — |
| 5 | `20260710000002_shared_maps_lockdown.sql` | Only after step 4 |
| 6 | `20260710000004_analytics_ingest_lockdown.sql` | Only after step 4 |

Each migration header includes rollback instructions. After applying, verify
RLS as an anonymous client, a normal authenticated user, and the admin —
verification queries are embedded in `...000005`.

`20260901000001_shared_rate_limit.sql` is additive and can be applied any
time, but until it is, the cross-instance rate limiter in `api/_lib/guard.js`
has nothing to call and fails open (only the per-instance limiter applies).
The `send-welcome` edge function must also be redeployed
(`supabase functions deploy send-welcome`) to pick up its lead-row check.

## Deployment & rollback

Pushes to `main` deploy via Vercel (`npm run build` → `dist/`, plus `api/`
functions). Rollback = redeploy the previous Vercel deployment (instant);
if a deploy-coupled migration was applied after the deploy being rolled
back, also run the rollback SQL from that migration's header.

## Testing

Vitest + React Testing Library (`tests/`, config in `vitest.config.js`).
Coverage focuses on regression protection: save/autosave race conditions,
claims request ordering/cancellation, provider pagination, shapefile
projection + CSV/GeoJSON parsing, storage failure/corruption handling,
local→cloud migration retries, API hardening, ArcGIS geometry conversion,
and shared-map access.

