fix(data): load bundled region packs in the browser; bound region lookups (#743)

The Natural Earth and neighborhood packs were loaded with a JSON-attributed
import(). The Vite dev server answers that request with a JavaScript module,
which the browser rejects, so every analyst region scope failed and fell
through to a geocode lookup that could take 12-28 s.

- src/data/bundledJson.js fetches the pack from a new URL(..., import.meta.url)
  asset in the browser; under Node the caller's attributed import runs.
- resolveRegionRingForQuery caps the geocode + admin-boundary rung at 3 s and
  returns { error: 'region-timeout' }; the lookup keeps running and fills the
  cache. The analyst query reports it as code 'region-timeout'. The Nepal
  locator opts out of the budget.
- Unit tests for the loader, the timeout and the analyst error; the behavior
  harness checks the Gulf of Mexico scope and retries Texas on region-timeout.

Co-authored-by: Bilawal Sidhu <[email protected]>
This commit is contained in:
Bilawal SidhuandBilawal Sidhu authored and GitHub committed 2026-09-24 01:52:41 -05:00
1 parent 513dc87a0e
commit ce671ce500
15 files changed
+263 -28

No files matched your search

+29 -1
View File
@@ -25,6 +25,9 @@ import {
import { unavailablePlaceSearch } from '../search/placeSearch.js';
import { isPickedWorldPosition } from '../data/scenePick.js';
/** Ms the analyst region lookup waits on geocode + admin boundary. */
export const REGION_FALLBACK_BUDGET_MS = 3_000;
/** Own annotation lookup caches and ranking of supplied feature candidates. */
export function createAnnotationResolver({
boundarySource,
@@ -1877,14 +1880,22 @@ export function createAnnotationResolver({
* resolve to a region-like boundary — the analyst engine reports that
* honestly rather than silently scoping to nothing.
*
* The geocode + admin-boundary rung is capped at `budgetMs`. Past it the
* call returns `{name, ring: null, error: 'region-timeout'}` so a voice
* answer is not held for tens of seconds; the lookup keeps running and
* fills the boundary cache, so asking again shortly is fast.
*
* @param {string} name e.g. "Texas", "the Alps", "France", "Gulf of Mexico"
* @param {AbortSignal} [signal]
* @returns {Promise<{name:string, ring:Array<[number,number]>}|null>}
* @param {object} [placeSearch]
* @param {{budgetMs?: number}} [options] `Infinity` waits for the lookup.
* @returns {Promise<{name:string, ring:Array<[number,number]>}|{name:string, ring:null, error:'region-timeout'}|null>}
*/
async function resolveRegionRingForQuery(
name,
signal,
placeSearch = unavailablePlaceSearch,
{ budgetMs = REGION_FALLBACK_BUDGET_MS } = {},
) {
signal = lifetime
? signal
@@ -1901,6 +1912,23 @@ export function createAnnotationResolver({
const ring = [...ne.polygons].sort((a, b) => b.length - a.length)[0];
if (ring?.length >= 3) return { name: ne.name, ring };
}
const lookup = resolveAdminRegionRing(q, signal, placeSearch);
if (!Number.isFinite(budgetMs)) return lookup;
let timer;
const timeout = new Promise((resolve) => {
timer = setTimeout(
() => resolve({ name: q, ring: null, error: 'region-timeout' }),
budgetMs,
);
});
try {
return await Promise.race([lookup, timeout]);
} finally {
clearTimeout(timer);
}
}
async function resolveAdminRegionRing(q, signal, placeSearch) {
const geo = await geocodePlace(q, null, signal, placeSearch).catch(
() => null,
);