A Cloudflare-first web application that helps EV drivers plan trips by visualizing safe driving ranges and suggesting virtual charging stops.
Live Demo: https://ev-overlay.pages.dev/ API Endpoint: https://ev-overlay-api.gfhdlcod02.workers.dev/api/route
- Trip Planning: Enter origin, destination, and EV parameters (SoC, range, reserve, driving mode)
- Geolocation Map Defaults: Auto-detects user location to set origin and center map (Thailand default)
- Safe Range Calculation: Deterministic conservative range estimates with safety margins
- Route Visualization: Interactive map with safe (green) and risky (red) segment coloring
- Charging Stops: Automatic placement of virtual charging stops along route
- Google Maps Handoff: One-click navigation with waypoints
- Responsive Design: Works on desktop and mobile devices
- Fast Performance: Route caching with 7-day TTL
- Rate Limiting: 60 requests/minute per IP for API protection
- Node.js 20+
- pnpm 8+
- Cloudflare account (for deployment)
# Clone the repository
git clone <repository-url>
cd ev-overlay
# Install dependencies
pnpm install
# Setup git hooks (Husky)
pnpm prepare
# Start development server
pnpm devThe development server will start at http://localhost:3000
# Build all packages
pnpm build
# Build specific package
pnpm --filter @ev/core build
pnpm --filter @ev/web build
pnpm --filter @ev/api build# Run all tests
pnpm test
# Run unit tests only
pnpm --filter @ev/core test
# Run integration tests
pnpm --filter @ev/api test
# Run E2E tests
pnpm test:e2e
# Run tests with coverage
pnpm test:coverage# Run linter
pnpm run lint
# Fix lint issues
pnpm run lint:fixThe codebase follows a feature-based architecture with clear separation of concerns:
ev-overlay/
├── packages/core/ # Pure deterministic TypeScript logic
│ ├── src/
│ │ ├── calculator/ # Safe range, stop placement algorithms
│ │ ├── types/ # Shared domain types
│ │ ├── url-builder/ # Google Maps URL generation
│ │ ├── utils/ # Haversine distance calculation
│ │ └── validators/ # Input validation
│ └── vitest.config.ts # Unit tests (85 tests, 100% coverage)
│
├── apps/web/ # Vue 3 + Vite frontend
│ ├── src/
│ │ ├── config/ # Centralized configuration
│ │ ├── features/ # Feature-based organization
│ │ │ ├── ev-params/ # EV parameter inputs
│ │ │ ├── map/ # Route map, geolocation
│ │ │ ├── trip-planning/ # Trip input, results
│ │ │ └── ui/ # Shared UI components
│ │ ├── services/ # API client, caching
│ │ ├── types/ # TypeScript type definitions
│ │ ├── utils/ # Utility functions
│ │ └── App.vue # Main application
│ ├── tests/e2e/ # Playwright E2E tests
│ └── .env.example # Environment configuration
│
├── workers/api/ # Cloudflare Worker API
│ ├── src/
│ │ ├── config/ # Centralized configuration
│ │ ├── features/ # Feature-based organization
│ │ │ ├── rate-limiting/ # Rate limiting handler
│ │ │ ├── routing/ # Route handlers, OSRM, cache
│ │ │ └── shared/ # Cross-cutting concerns (CORS)
│ │ └── index.ts # Worker entry point
│ ├── tests/integration/ # Integration tests
│ └── .env.example # Environment configuration
│
└── specs/ # Feature specifications
├── 001-smart-ev-overlay/
├── 002-rate-limiting/
├── 003-geolocation-map-defaults/
├── 004-simplify-search-form/
└── 005-refactor-structure/ # Current architecture
| Package | Responsibility | Constraints |
|---|---|---|
packages/core |
Pure business logic | Zero browser/Node dependencies |
apps/web |
UI and map rendering | No direct provider calls |
workers/api |
Edge API and caching | Secrets server-side only |
Each package organizes code by feature rather than layer:
features/- Co-locate related components, composables, and storesservices/- API clients and external integrationsconfig/- Centralized configuration loaderstypes/- TypeScript type definitions
// Internal imports use @/ alias
import { useTripInput } from '@/features/trip-planning/composables/useTripInput'
import { API_CONFIG } from '@/config'
// Cross-package imports use package names
import type { Route } from '@ev/core'
import { calculateSafeRange } from '@ev/core'User Input → Validation → Safe Range Calculation → Route Fetch →
Stop Placement → Segment Classification → Map Visualization →
Google Maps Handoff
The core formula for calculating safe driving range:
safeRangeKm = ((socNow - reserveArrival) / 100) * (range100Km / factor)
Where:
socNow: Current state of charge (0-100%)reserveArrival: Minimum charge on arrival (default 20%)range100Km: Vehicle range at 100% chargefactor: Driving efficiency factor (Eco=1.05, Normal=1.15, Highway=1.25)
- Calculate safe range with buffer (safeRange - 10km)
- Accumulate route distance from origin
- When accumulated >= threshold, place stop
- Assume 80% charge after each stop
- Repeat until destination reached
- Maximum 5 stops
Fetch route data with caching.
Query Parameters:
origin: Origin coordinates aslat,lngdestination: Destination coordinates aslat,lng
Response:
{
"route": {
"origin": { "lat": 37.7749, "lng": -122.4194 },
"destination": { "lat": 34.0522, "lng": -118.2437 },
"distanceKm": 612.5,
"durationMin": 350,
"geometry": {
"type": "LineString",
"coordinates": [[lng, lat], ...]
}
}
}apps/web (.env.local):
VITE_API_URL=http://localhost:8787/api
workers/api (.env):
OSRM_BASE_URL=https://router.project-osrm.org
Create a KV namespace in Cloudflare:
wrangler kv:namespace create "ROUTE_CACHE"Update wrangler.toml with the namespace ID.
# Linux/Mac
./deploy.sh
# Windows
deploy.batSee DEPLOYMENT.md for step-by-step guide and GitHub Actions setup.
Deployments are triggered automatically when a version tag is pushed to main.
Release Workflow:
# 1. Create a release branch
git checkout -b release/v1.3.0
# 2. Bump version and sync to all packages
npm version 1.3.0 --no-git-tag-version
pnpm version:sync
# 3. Commit version changes
git add -A
git commit -m "chore(release): v1.3.0"
# 4. Push and create PR
git push -u origin release/v1.3.0
gh pr create --title "chore(release): v1.3.0" --body "Version bump"
# 5. After PR is merged, create and push tag
git checkout main
git pull
git tag v1.3.0
git push --tagsSee DEPLOYMENT.md for detailed version management documentation.
- Safe range calculation
- Stop placement algorithm
- Segment classification
- Distance accumulation
- Input validation
- Google Maps URL builder
Coverage: 100%
- Route endpoint
- KV cache hit/miss
- OSRM normalization
- Error handling
- Complete user flow
- Geolocation flows (permission grant/deny, auto-populate, map recenter)
- Mobile viewport
- Error states
- Google Maps handoff
This project follows the Smart EV Overlay Constitution:
- ✅ Cloudflare-First: All infrastructure on Cloudflare
- ✅ Conservative Safety-First: 20% reserve, 10km buffer defaults
- ✅ Deterministic Core: Pure functions, no ML
- ✅ Security & Privacy: Input validation, no PII
- ✅ Separation of Concerns: core/web/api packages
- ✅ Reliability: Timeouts, retries, caching
- ✅ Definition of Done: Tests, error states
- ✅ Phase-Gated: MVP Phase 1 complete
- ✅ Playwright Testing: E2E tests for critical paths
- ✅ Code Quality: ESLint/Prettier, strict TS
- ✅ Code Security: No secrets, dependency scanning
- ✅ PR Workflow: Husky pre-commit hooks enforce branch protection
Please read CONTRIBUTING.md for detailed guidelines.
Quick summary:
- Direct commits to
mainare blocked - use Pull Requests - Husky pre-commit hook runs tests automatically
- Follow the Constitution principles
- Write tests for new features
- Follow existing code style
MIT