How published snapshots reach browsers: the public R2 domain for bulk reads, and the transit-data-proxy Worker for same-origin snapshot access and the per-provider KPI summary.
| Reference | Value |
|---|---|
| Subject and boundary | Routes, response semantics, CORS, edge caching and the /api/v1/kpis contract. The web Worker's own routes are on ‣. |
| Verified source | worker.js, snapshot-response.js, kpis.js, wrangler.toml, r2-cors.json, configure-data-edge.mjs at main 9733eb26d92533f56cdc8bc46b81b93fbb5987c8, checked November 10, 2026. |
| Support state | Implemented. No database connection and no secrets. |
| Path | Used by | Behavior |
|---|---|---|
https://data.yesid.dev/v1/... |
Browser bulk reads and absolute URLs in manifests | R2 custom domain. A Cloudflare cache rule caches /v1/*.json while respecting the publisher's Cache-Control |
https://transit.yesid.dev/data/v1/... |
Same-origin reads and the web dev proxy | transit-data-proxy reads the SNAPSHOTS binding (transit-snapshots) |
https://transit.yesid.dev/api/v1/kpis |
KPI consumers | Computed summary for one provider |
R2 CORS allows any origin to GET and HEAD with Range, If-None-Match and If-Modified-Since, and exposes ETag, Content-Range, Cache-Control and related headers.
| Request | Response |
|---|---|
OPTIONS |
204 preflight |
Method other than GET, HEAD |
405 with Allow |
Undecodable path, path outside /data/v1/, a key containing .., missing object |
404 |
/data/v1/sto/* |
410 (provider unavailable for public access) |
Unknown /api/v1/* |
Uncacheable 404 |
/data/v1/<key> |
Object from R2 with stored metadata, ETag, conditional (304, 412) and single-range (206, 416) support |
GET /api/v1/kpis returns snapshotAt, freshnessS, vehicles, avgDelayS, coverage, routesLive, routesTotal and up to five topRoutes (route, vehicles, avgDelayS). HEAD returns the headers only. Breaking field changes need a new endpoint version.
?provider= selects the provider. A supplied value must match an id in v1/providers.json (memoized for one live TTL): an unknown provider returns an uncacheable 404, and an unreadable catalog returns the cold 503. A failed providers.json read is cached for 20 s, so ?provider= requests keep returning 503 for up to 20 s. A request without the parameter defaults to stm and does not read the catalog. Each provider has its own in-isolate state (core memo, in-flight rebuild, route totals, failure cooldown) and its own Cache API key, core-v3/<provider> (kpis.js).
Source under v1/<provider>/ |
Contributes |
|---|---|
live/vehicles.json |
Vehicle count (vehicles.length, which includes vehicles with no trip, unlike network.json's vehicles in service), live route count, routes ranked by vehicles; its timestamp anchors snapshotAt |
live/trips.json |
Mean of finite whole-minute trip delays (delay_min * 60), not second-level delays, network-wide and per top route |
live/network.json |
coverage_pct as a fraction |
static/routes_index.json |
Bus route count (route type 3), so rail lines without vehicle tracking are excluded; every route when the index carries no types |
A source is unusable at age ≥60 seconds (three 20-second live TTLs) or more than 60 seconds in the future. An unusable vehicle anchor returns 503 with Retry-After: 20; unusable trip or network data nulls only the fields that depend on it. Means round once to whole seconds, half away from zero (−30.5 s becomes −31 s).
Rebuilds are shared per isolate and provider and memoized for one 20-second live TTL, kept across isolate recycles with the Cache API. Usable older entries serve while refreshing; failed rebuilds are throttled for 10 seconds; route totals are cached for an hour. Responses are Cache-Control: no-store because freshness is evaluated at serve time, and X-Kpis-Cache reports miss, hit or stale.
bun run --cwd apps/data-proxy check
bun run --cwd apps/data-proxy test
bash apps/data-proxy/smoke.sh # production route smoke; CANONICAL_BASE, FALLBACK_BASE, APEX_BASE override hosts
Tests use local R2 and Cache API substitutes. Deployment is on Deployment and release.
kpiSampleState and the agreement examples define additive v1 shape and hero eligibility. Counts are non-negative safe integers; delay seconds and route totals may be null, coverage is a fraction in [0,1] or null, and timestamps use UTC. Extra fields are allowed. A valid 503 carries error: pipeline_cold, a detail string and Retry-After: 20. Shape failure is distinct from a valid but incomplete 200.
The public probe counts a 200 as live-eligible only with delay, coverage and route total plus at least one top-route delay. A valid incomplete 200 or expected cold 503 is a demo sample; malformed responses are probe failures. This stateless check does not implement a browser hold policy.
Before a cached trip/network lane would newly become null because it crossed 60 seconds, the proxy joins a fresh single-flight R2 rebuild, including on a Cache API cold-isolate hit. On read failure it keeps a usable vehicle anchor, nulls only expired dependent values and respects the 10-second failure cooldown. Fresh vehicle data is required; X-Kpis-Cache is diagnostic, not a freshness guarantee. The cache key includes the current internal schema version and the provider.