This file provides guidance to AI agents when working with code in this repository.
A single Cloudflare Worker that backs the omaps.app domain for Organic Maps. It does three things, in priority order per request:
- Redirects — short vanity paths (
/g→ Google Play,/ios→ App Store,/.well-known/*→ association files, etc.). - Static assets — serves everything under
public/(install pages, app-association JSON, placemark icons). - ge0 short-link decoding — turns
omaps.app/ENCODEDCOORDS/PinNameinto lat/lon/zoom and renders an OSM map page that deep-links into the mobile app via theom:/URL scheme.
npm i # install (also pulls wrangler)
npx wrangler dev # local dev server (DEBUG=true)
npm run build # esbuild bundles src/index.ts -> workers-site/index.js
npm test # jest (TS compiled via test/esbuild-transform.cjs)
npx jest -t 'zoom' # run a single test by name substring
npm run lint # Oxlint src + TypeScript 7 type checking
npm run typecheck # tsc --noEmit (TypeScript 7)
npm run format # prettier over src/**/*.{ts,tsx,json} and public/*.json
npm run format:check # check Prettier formatting without modifying files
npm run upgrade # npm-check-updates --peer -u && npm install
npx wrangler deploy --env omaps # manual prod deploy (README shows the older `publish`)npm test runs Jest. TypeScript is transpiled by a tiny esbuild-based transformer (test/esbuild-transform.cjs, wired via jest.config.js) — the same bundler as the production build, so there is no ts-jest/babel dependency or dependency on TypeScript's JavaScript compiler API. The repo uses TypeScript 7 and Oxlint. Tests live in test/*.test.ts and import straight from src/.
test/api.test.ts covers the pure units — decodeLatLonZoom, normalizeZoom, normalizeNameAndTitle (including the Windows-1251 fallback), CLEAR_COORDINATES_REGEX — plus onGe0Decode end-to-end (encoded + clear-text paths, HTML-escaping, out-of-range rejection). To test a new helper, export it from src/ge0.ts — only exported symbols are reachable from tests. Run one test with npx jest -t '<name substring>'.
npm run lint (Oxlint over src/ + tsc --noEmit) is expected to pass — CI runs it on PRs — so treat a lint failure as a real regression, not pre-existing noise. .oxlintrc.json preserves the migrated code-quality rules and Worker globals; Prettier handles formatting through npm run format:check in CI.
handleFetchEvent runs on every request:
- If the path is in
OMAPS_REWRITE_RULESand its value starts withhttp, return a302redirect. - Otherwise try
getAssetFromKV(static file frompublic/).OMAPS_REWRITE_RULESvalues that are paths (e.g./→/get.html,/.well-known/apple-app-site-association→/apple-app-site-association.json) are applied here as internal rewrites viamapRequestToAsset. - If no asset matches, fall through to the ge0 decoder: load the
/ge0.htmltemplate and callonGe0Decode(template, url).
The .well-known/* rewrite rules exist because wrangler does not upload hidden directories or symlinks to the asset bucket, so those paths are mirrored to non-hidden copies in public/.
onGe0Decode has two decode paths:
- Clear-text coordinates — if the path matches
CLEAR_COORDINATES_REGEX(literallat/lon/zoom/namein the URL), use those directly. - Encoded ge0 — otherwise
decodeLatLonZoomruns the Organic Maps ge0 algorithm: first char encodes zoom, remaining chars pack interleaved lat/lon bits decoded through thebase64Reverselookup table (Organic Maps' custom base64 alphabet,-and_for the last two symbols). This mirrors the C++ decoder in the main organicmaps repo — keep it bit-for-bit compatible.
Both paths then:
- Normalize the pin name (
normalizeNameAndTitle):+/_→ space,decodeURIComponent, with a Windows-1251 fallback for malformed encodings (vk.com mangles some shared links), falling back to😃. - Escape output with
encodeHTML(XSS prevention) before injecting. - Fill template tokens in
public/ge0.htmlviareplaceInTemplate. Use context-specific escaped tokens:${name}for HTML,${nameJs}for JavaScript strings,${appUriAttr}for HTML attributes, and${appUriJs}for JavaScript strings. If you add a token in the HTML, it silently renders empty unless you pass it here.
Coordinates outside valid lat/lon ranges throw; the caller turns a throw into a 500 (dev) or a redirect to organicmaps.app (prod).
DEBUG is a Worker var set in wrangler.toml, and it changes behavior in two important ways:
| dev (default env) | prod (env.omaps) |
|
|---|---|---|
DEBUG |
true |
false |
| Redirect/rewrite rules | apply on any hostname | apply only when hostname === 'omaps.app' |
| Errors | return the real message as 500 |
redirect to https://organicmaps.app |
| Asset cache | bypassed | normal CF caching |
| Worker name / route | url-processor on *.workers.dev |
url-processor-omaps on omaps.app/* |
So a rewrite rule you add will "work everywhere" in dev but only fire on omaps.app in prod — test with the real host in mind.
workers-site/index.jsis the esbuild output and is gitignored — never edit it by hand; editsrc/and rebuild.wrangler.toml's[build]step runsnpm run buildautomatically on deploy.- Pushing to
mastertriggers.github/workflows/deploy-master-to-prod.yml, which deploys theomaps(production) environment viacloudflare/wrangler-action. - CI (
.github/workflows/check.yml) runs on PRs with Node 24 (matching.nvmrc) and runsnpm run format:check,npm run lint,npm run build, thennpm test.