refactor: own application request operations and surface caches

This commit is contained in:
Sameh Khamis committed 2026-09-14 14:49:04 -07:00
1 parent 310809e8f1
commit ac977bd8bc
53 files changed
+4388 -3317

No files matched your search

+19 -18
View File
@@ -13,10 +13,10 @@ import { contextModeWord } from '../contextModePolicy.js';
import { createAnalystEngine } from '../data/analystEngine.js';
import { layerFeedState } from '../data/manager.js';
import { initCameraVerbs, moveCamera, flyRoute, interruptCameraMotion, adjustOrbitRange } from '../cameraVerbs.js';
import { cachedGroundFloor, warmGroundFloor } from '../data/groundFloor.js';
import * as defaultFloorServices from '../data/groundFloor.js';
import { isPickedWorldPosition } from '../data/scenePick.js';
import { unavailablePlaceSearch } from '../search/placeSearch.js';
import { resolveRegionRingForQuery } from '../annotations/annotationResolver.js';
import * as defaultAnnotationResolver from '../annotations/annotationResolver.js';
import { normalizeRadioCountryInput } from '../data/radioCountry.js';
import { TR3B_CLASS } from '../data/tr3bRegistry.js';
@@ -263,7 +263,11 @@ const BASEMAP_CONTEXT_WAIT_MS = 1500;
const viewTargetCache = new WeakMap();
/** Create application actions over the supplied scene and services. */
export function createGevActionRunner({ viewer, styleManager, dataManager, sceneDirector = null, annotations = null, placeSearch = unavailablePlaceSearch }) {
export function createGevActionRunner({ viewer, styleManager, dataManager, sceneDirector = null, annotations = null, placeSearch = unavailablePlaceSearch, floorServices = defaultFloorServices, annotationResolver = defaultAnnotationResolver, searchNavigation = searchAndFlyTo }) {
// Voice enable times and analyst follow-up memory belong to this runner.
const _layerEnabledAt = new Map();
let analystEngine;
const resolveRegionRing = (name) => annotationResolver.resolveRegionRingForQuery(name, undefined, placeSearch);
installViewTargetPrewarm(viewer);
initCameraVerbs(viewer, getViewTargetCartesian);
return async function runGevAction(name, rawArgs = {}, runOptions = {}) {
@@ -486,7 +490,7 @@ export function createGevActionRunner({ viewer, styleManager, dataManager, scene
};
const nearest = await createAnalystEngine(analystProviders(viewer, dataManager, {
recordLimitByLayer: { [layerId]: Number.MAX_SAFE_INTEGER }, placeSearch,
recordLimitByLayer: { [layerId]: Number.MAX_SAFE_INTEGER }, placeSearch, resolveRegionRing,
})).query({
layers: [layerId],
scope: { kind: 'view' },
@@ -745,7 +749,7 @@ export function createGevActionRunner({ viewer, styleManager, dataManager, scene
if (name === 'fly_to_location') {
return flyToRequestedLocation(viewer, args, {
placeSearch, signal: runOptions.signal,
placeSearch, searchNavigation, signal: runOptions.signal,
runImmediate: typeof styleManager?.runImmediateLocationNavigation === 'function'
? (navigate) => styleManager.runImmediateLocationNavigation(navigate)
: null,
@@ -791,7 +795,8 @@ export function createGevActionRunner({ viewer, styleManager, dataManager, scene
}
if (name === 'analyst_query') {
return runAnalystQuery(viewer, dataManager, args, placeSearch);
analystEngine ||= createAnalystEngine(analystProviders(viewer, dataManager, { placeSearch, resolveRegionRing }));
return runAnalystQuery(analystEngine, dataManager, args, _layerEnabledAt);
}
if (name === 'move_camera') {
@@ -804,9 +809,9 @@ export function createGevActionRunner({ viewer, styleManager, dataManager, scene
return flyRoute(
annotations?.list?.() || [],
args,
(lat, lon) => cachedGroundFloor(lat, lon),
(lat, lon) => floorServices.cachedGroundFloor(lat, lon),
(navigate) => runManagedVoiceNavigation(styleManager, 'route', 'fly_route', navigate),
(cells) => warmGroundFloor(cells),
(cells) => floorServices.warmGroundFloor(cells),
);
}
@@ -2166,7 +2171,7 @@ function normalizeStyle(value) {
}
async function flyToRequestedLocation(viewer, args, {
placeSearch = unavailablePlaceSearch, signal,
placeSearch = unavailablePlaceSearch, searchNavigation = searchAndFlyTo, signal,
onStart = null,
runImmediate = null,
beginDeferred = null,
@@ -2281,7 +2286,7 @@ async function flyToRequestedLocation(viewer, args, {
const generation = typeof beginDeferred === 'function' ? beginDeferred() : null;
if (generation === false) return cancelled(query);
const managedDeferred = typeof reassertDeferred === 'function';
const destination = await searchAndFlyTo(viewer, query, {
const destination = await searchNavigation(viewer, query, {
placeSearch, signal,
...(rangeM ? { range: rangeM } : {}),
forceClose: args.viewMode === 'close',
@@ -3218,9 +3223,6 @@ function clampNumber(value, min, max, fallback) {
* the voice payload. One engine per runner keeps follow-up memory
* ("which of those is closest?") scoped to the session.
*/
/** layerId → epoch ms of last voice-driven enable (analyst warm-up honesty). */
const _layerEnabledAt = new Map();
let _analystEngine = null;
/** Layers whose loaded set follows the camera, so a loaded count is not a world count. */
const VIEWPORT_LOADED_LAYERS = new Set(['flights']);
@@ -3239,7 +3241,7 @@ function activeContactsWindow(dataManager) {
}
}
function analystProviders(viewer, dataManager, { recordLimitByLayer = null, placeSearch = unavailablePlaceSearch } = {}) {
function analystProviders(viewer, dataManager, { recordLimitByLayer = null, placeSearch = unavailablePlaceSearch, resolveRegionRing = (name) => defaultAnnotationResolver.resolveRegionRingForQuery(name, undefined, placeSearch) } = {}) {
return {
getRecords(layerKey) {
const layer = dataManager.layers.get(layerKey);
@@ -3251,7 +3253,7 @@ function analystProviders(viewer, dataManager, { recordLimitByLayer = null, plac
? (mod.getAnalystRecords(requestedLimit) || [])
: (mod.getAnalystRecords() || []);
},
resolveRegionRing: (name) => resolveRegionRingForQuery(name, undefined, placeSearch),
resolveRegionRing,
/**
* The active Contacts subject, when there is one — the centre the operator
* is reasoning about while Contacts is up. Null whenever Contacts is off,
@@ -3285,9 +3287,8 @@ function analystProviders(viewer, dataManager, { recordLimitByLayer = null, plac
};
}
async function runAnalystQuery(viewer, dataManager, args = {}, placeSearch = unavailablePlaceSearch) {
if (!_analystEngine) _analystEngine = createAnalystEngine(analystProviders(viewer, dataManager, { placeSearch }));
const result = await _analystEngine.query({
async function runAnalystQuery(analystEngine, dataManager, args = {}, _layerEnabledAt) {
const result = await analystEngine.query({
layers: Array.isArray(args.layers) ? args.layers : undefined,
scope: args.scope,
filters: Array.isArray(args.filters) ? args.filters : [],