Moveet: incidents, recording & replay, icon rail UI, and 500+ tests

Published: (March 16, 2026 at 06:38 AM EDT)
5 min read
Source: Dev.to

Source: Dev.to

What shipped

🚦 Fleet management

Vehicles can now be grouped into named, colour‑coded fleets. You create a fleet, assign vehicles to it, and the UI colours routes and markers per fleet – handy for simulating multiple operators sharing the same road network.

REST API

POST   /fleets
GET    /fleets
DELETE /fleets/:id
POST   /fleets/:id/assign
POST   /fleets/:id/unassign

WebSocket events

  • fleet:created
  • fleet:deleted
  • fleet:assigned

⚠️ Incidents and dynamic rerouting

Drop a road incident anywhere on the network. Every vehicle currently routing through the affected segment is rerouted live – A* runs again from the vehicle’s current position, routing around the blocked edge.

REST API

POST   /incidents          # create incident, triggers rerouting
GET    /incidents          # list active incidents
DELETE /incidents/:id      # clear it, vehicles return to normal paths
POST   /incidents/random   # random incident for testing

WebSocket event

  • vehicle:rerouted – emitted for each vehicle that is rerouted

Incident markers appear on the map.


🎬 Session recording and replay

Every simulation session can be recorded to a timestamped NDJSON file. The format is a header line followed by one event per line (direction assignments, vehicle snapshots, incidents, …).

REST API

POST   /recording/start
POST   /recording/stop
GET    /recordings

Replay control API

POST   /replay/start    { "file": "path/to/recording.ndjson" }
POST   /replay/pause
POST   /replay/resume
POST   /replay/stop
POST   /replay/seek     { "timestamp": 12000 }
POST   /replay/speed    { "speed": 2 }
GET    /replay/status

Playback supports 1×, 2×, and 4× speed. The UI shows an interpolated progress bar that advances smoothly between server ticks, avoiding jumps every 500 ms.


🖥 UI redesign – icon rail + panel sidebar

The old floating overlay has been replaced by an icon rail on the left edge – a vertical strip of icon buttons that each toggle a panel.

IconPanel
🚗Vehicles – list, filter, select
🗂Fleets – create, assign, colour
⚠️Incidents – active list with badge count
Recordings – start/stop/browse/replay
👁Visibility – toggle map layers
Speed – simulation speed controls
⚙️Adapter – hot‑swap source/sink plugins

The bottom dock holds live‑simulation controls (play/stop/reset/record) and collapses into a replay transport bar during playback.

All panel components are built from a shared set of primitives (PanelShell, PanelHeader, PanelSection) and a unified theme token set, so every panel looks consistent without per‑component ad‑hoc styling.


✅ Test coverage – from 410 to 502 tests

The simulator test suite grew significantly. New files added:

FileWhat it covers
rateLimiter.test.tsWindow enforcement, 429 responses, per‑IP tracking, cleanup interval
helpers.test.tscalculateBearing, interpolatePosition, calculateDistance, nonCircularRouteEdges, estimateRouteDuration
serializer.test.tsserializeVehicle with and without fleet assignment
config.test.tsverifyConfig – missing file, port range, speed ordering, all numeric constraints
SimulationController.test.tsFull lifecycle: start, stop, getStatus, setOptions, getVehicles, getInterval, all replay methods

Testing pattern worth notingSimulationController tests stub VehicleManager.prototype.setRandomDestination to prevent A* calls on the tiny test‑network fixture, then restore it. Replay tests write a minimal NDJSON file to a temporary directory before each test, and everything is cleaned up in afterEach.


📡 WebSocket – 100 ms batching + back‑pressure

Vehicle position updates are now batched every 100 ms before broadcast. If a client’s write buffer is backed up, the broadcaster skips that client rather than growing an unbounded queue. This keeps the simulator usable with 50–100 vehicles without connection degradation.

All broadcast event types

vehicles          – batched position array (100 ms window)
status            – simulation state (running / ready / interval)
options           – current StartOptions
heatzones         – HeatZoneFeature[]
direction         – active dispatch assignment
waypoint:reached  – vehicle reached a waypoint
route:completed   – vehicle completed its full route
reset             – simulation was reset
fleet:created / fleet:deleted / fleet:assigned
incident:created / incident:cleared
vehicle:rerouted  – live A* reroute triggered

🔧 CI improvements

The GitHub Actions workflow previously ran npm ci three times in parallel (once per job). It now has a dedicated setup job that installs once and caches node_modules keyed on package-lock.json. Lint, test, and build jobs restore from that cache. The build job also caches .turbo/ with a per‑commit key and branch‑level restore keys.


Architecture diagram (updated)

┌──────────────────────────────────────┐
│  apps/ui                             │
│  React 19 · D3 7 · Vite · TS 5.8    │
│  Mercator SVG map, 1×–15× zoom       │
│  Icon rail · Panel sidebar · Dock    │
└──────────────┬───────────────────────┘

Architecture Overview

               │ REST + WebSocket

┌──────────────────────────────────────┐
│  apps/simulator                      │
│  Express 4 · ws 8 · Turf.js 7       │
│  GeoJSON graph · A* · LRU cache      │
│  WS broadcaster · 100ms batching     │
└──────────────┬───────────────────────┘
               │ GET /vehicles · POST /sync

┌──────────────────────────────────────┐
│  apps/adapter (optional)             │
│  Source plugins: static / graphql /  │
│  rest / mysql / postgres             │
│  Sink plugins: console / graphql /    │
│  rest / redpanda / redis / webhook   │
└──────────────────────────────────────┘

Quick start

curl -O https://raw.githubusercontent.com/ivannovazzi/moveet/main/docker-compose.ghcr.yml
docker compose -f docker-compose.ghcr.yml up

Open . No configuration, no API keys required.

Repository:


Feel free to ask questions about the A implementation, the D3 renderer, or the recording format in the comments.*

0 views
Back to Blog

Related posts

Read more »

Jemalloc un-abandoned by Meta

- Meta recognizes the long‑term benefits of jemalloc, a high‑performance memory allocator, in its software infrastructure. - We are renewing focus on jemalloc,...

Meta’s renewed commitment to jemalloc

Meta recognizes the long‑term benefits of jemalloc, a high‑performance memory allocator, in its software infrastructure. We are renewing focus on jemalloc, aimi...