From 2f7bd65dc8caaa44f7b4540a08c504a3fe7acb3e Mon Sep 17 00:00:00 2001 From: Ayobami Haastrup <47716486+AyobamiH@users.noreply.github.com> Date: Sun, 6 Sep 2026 01:20:00 +0100 Subject: [PATCH] fix(data): enforce canonical city slugs --- data/places.schema.json | 1 + data/places/library.json | 2 +- data/places/sat_centre.json | 8 ++++---- scripts/validate-places.mjs | 13 +++++++++++++ src/app/docs/(article)/places-api/page.tsx | 2 +- 5 files changed, 20 insertions(+), 6 deletions(-) diff --git a/data/places.schema.json b/data/places.schema.json index 122b0a7..288e32f 100644 --- a/data/places.schema.json +++ b/data/places.schema.json @@ -24,6 +24,7 @@ "city": { "type": "string", "minLength": 1, + "pattern": "^[a-z0-9]+(_[a-z0-9]+)*$", "description": "Lowercase, underscore-separated slug, e.g. \"mumbai\", \"navi_mumbai\". Any city worldwide is welcome, not a fixed enum." }, "lat": { diff --git a/data/places/library.json b/data/places/library.json index 7a18d4f..7fec9fa 100644 --- a/data/places/library.json +++ b/data/places/library.json @@ -267,7 +267,7 @@ "id": "xiamen-library-01", "name": "厦门图书馆集美新城馆东南门", "type": "library", - "city": "厦门", + "city": "xiamen", "lat": 24.59441, "lng": 118.05789, "address": "福建省厦门市集美区诚毅中路380号", diff --git a/data/places/sat_centre.json b/data/places/sat_centre.json index b3f60c5..7ebbad8 100644 --- a/data/places/sat_centre.json +++ b/data/places/sat_centre.json @@ -250,7 +250,7 @@ "id": "delhi-ncr-sat-01", "name": "American Embassy School", "type": "sat_centre", - "city": "new delhi", + "city": "new_delhi", "lat": 28.598015, "lng": 77.182659, "address": "CHANDRAGUPTA MARG, CHANAKYAPURI, NEW DELHI, DELHI", @@ -263,7 +263,7 @@ "id": "delhi-ncr-sat-02", "name": "Amity International School", "type": "sat_centre", - "city": "new delhi", + "city": "new_delhi", "lat": 28.613895, "lng": 77.209006, "address": "SECTOR 7, PUSHP VIHAR, NEW DELHI, DELHI", @@ -276,7 +276,7 @@ "id": "delhi-ncr-sat-03", "name": "The British School", "type": "sat_centre", - "city": "new delhi", + "city": "new_delhi", "lat": 28.597247, "lng": 77.180085, "address": "DR. JOSE P. RIZAL MARG, CHANAKYAPURI, NEW DELHI, DELHI", @@ -2330,7 +2330,7 @@ "id": "04489", "name": "Academy at Palumbo", "type": "sat_centre", - "city": "philadephia", + "city": "philadelphia", "lat": 39.9400517, "lng": -75.1615327, "address": "1100 CATHARINE STREET", diff --git a/scripts/validate-places.mjs b/scripts/validate-places.mjs index 71eeb81..cedebe9 100644 --- a/scripts/validate-places.mjs +++ b/scripts/validate-places.mjs @@ -10,6 +10,7 @@ * - Unique id within each file and across all files * - type is one of the schema's enum values * - type matches the filename it lives in + * - city is a lowercase, underscore-separated slug * - id matches the -- convention, or is a bare * numeric id (the College Board SAT centre code convention) * - lat/lng are numbers within the schema's min/max bounds @@ -43,6 +44,7 @@ const BOUNDS = { maxLng: schema.properties.lng.maximum, }; const GMAPS_RE = new RegExp(schema.properties.gmaps_link.pattern); +const CITY_SLUG_RE = new RegExp(schema.properties.city.pattern); // --, allowing multi-segment city/type prefixes // (e.g. "delhi-ncr-sat-01", "mum-airport-terminal-01"), or a bare numeric id @@ -155,6 +157,17 @@ for (const file of files) { ); } + // city must use the canonical lowercase, underscore-separated slug form + if ( + r.city !== undefined && + (typeof r.city !== "string" || !CITY_SLUG_RE.test(r.city)) + ) { + err( + loc, + `city must be a lowercase, underscore-separated slug, got ${JSON.stringify(r.city)}`, + ); + } + // valid_till format, when present if (r.valid_till !== undefined && !isRealIsoDate(r.valid_till)) { err(loc, `valid_till must be a real ISO date (YYYY-MM-DD), got "${r.valid_till}"`); diff --git a/src/app/docs/(article)/places-api/page.tsx b/src/app/docs/(article)/places-api/page.tsx index 157c602..4cf71cb 100644 --- a/src/app/docs/(article)/places-api/page.tsx +++ b/src/app/docs/(article)/places-api/page.tsx @@ -46,7 +46,7 @@ const FILTERS: { name: string; type: string; notes: string }[] = [ name: "city", type: "string", notes: - "Case-insensitive; spaces and hyphens are normalized to the dataset's underscore slugs (e.g. `New Delhi` matches `new delhi`). No matching city returns an empty `data` array.", + "Case-insensitive; spaces and hyphens are normalized to the dataset's underscore slugs (e.g. `New Delhi` matches `new_delhi`). No matching city returns an empty `data` array.", }, { name: "category",