From 42b17ffab64d6e9674edbd3ecdc2aad93ac703e3 Mon Sep 17 00:00:00 2001 From: Sameh Khamis Date: Tue, 3 Nov 2026 09:40:15 -0800 Subject: [PATCH] Keep the context id in Core's entity records instead of on picked objects registerEntityContext stamped __gevContextId on the entities, primitives and carriers it registered, and selection and pick resolution read it back. It is now the contextId field of the entity's Core record. --- src/data/contextStore.js | 8 +++++--- src/data/contextStore.test.mjs | 4 ++-- src/data/pickedContext.js | 8 ++++---- src/layers/localAdsb/index.test.mjs | 6 +++--- src/voice/gevActions.test.mjs | 8 ++++---- 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/data/contextStore.js b/src/data/contextStore.js index e0b14b2ba..2f2847b24 100644 --- a/src/data/contextStore.js +++ b/src/data/contextStore.js @@ -1,3 +1,5 @@ +import { entityRecord, readEntityRecord } from './entityRecords.js'; + const STORE_KEY = '__gevContextStore'; function createStore() { @@ -36,14 +38,14 @@ export function registerEntityContext(entity, metadata) { entity, updatedAt: Date.now(), }; - entity.__gevContextId = metadata.id; + entityRecord(entity).contextId = metadata.id; store.entities.set(metadata.id, record); return record; } export function selectEntityContext(entity) { const store = getContextStore(); - const contextId = entity?.__gevContextId; + const contextId = readEntityRecord(entity)?.contextId; if (!contextId || !store.entities.has(contextId)) return null; store.selectedEntityId = contextId; store.selectedAt = Date.now(); @@ -84,7 +86,7 @@ export function selectTrackedSubjectContext(metadata) { store.entities.delete(key); } // Reuse the existing carrier so a per-poll refresh does not churn identity. - const carrier = store.entities.get(id)?.entity || { __gevContextId: id }; + const carrier = store.entities.get(id)?.entity || {}; const record = registerEntityContext(carrier, { ...metadata, id }); if (!record) return null; store.selectedEntityId = id; diff --git a/src/data/contextStore.test.mjs b/src/data/contextStore.test.mjs index e9631133d..cb9f75eed 100644 --- a/src/data/contextStore.test.mjs +++ b/src/data/contextStore.test.mjs @@ -96,7 +96,7 @@ test('a sibling tracking layer keeps its own subject slot', () => { test('the per-poll refresh updates values without stealing the selection', () => { withWindow(() => { selectTrackedSubjectContext(flightSubject('aaa001')); - const other = registerEntityContext({ __gevContextId: 'dc-7' }, { + const other = registerEntityContext({}, { id: 'dc-7', layerId: 'local-datacenters', label: 'Datacenter 7', }); selectEntityContext(other.entity); @@ -130,7 +130,7 @@ test('deselecting a contact releases the shared slot', () => { test('clearing one tracking layer leaves another layer selection alone', () => { withWindow(() => { - const other = registerEntityContext({ __gevContextId: 'dam-3' }, { + const other = registerEntityContext({}, { id: 'dam-3', layerId: 'local-dams', label: 'Dam 3', }); selectEntityContext(other.entity); diff --git a/src/data/pickedContext.js b/src/data/pickedContext.js index 28c303564..fb79c3f3a 100644 --- a/src/data/pickedContext.js +++ b/src/data/pickedContext.js @@ -1,4 +1,5 @@ import { getContextStore, isContextRecordActive } from './contextStore.js'; +import { readEntityRecord } from './entityRecords.js'; import { findPickOwner, resolvePickId } from './pickRegistry.js'; /** @@ -7,8 +8,8 @@ import { findPickOwner, resolvePickId } from './pickRegistry.js'; * * Two lanes, in order: * 1. Context-store carriers. Entity-backed layers (infrastructure GeoJSON, - * selected vessels, fires, installations) stamp `__gevContextId` on the - * picked object, and the store record already carries the label and + * selected vessels, fires, installations) write a `contextId` into the + * picked object's entity record, and the store record already carries the label and * position voice uses everywhere else. * 2. Pick owners. Primitive-backed layers (aircraft, satellites, vessels) * register an ownership predicate in `pickRegistry`; the owning layer's @@ -52,8 +53,7 @@ function contextCarrierRecord(picked, dataManager) { picked?.primitive?.id, picked?.primitive, ]) { - const contextId = - carrier && typeof carrier === 'object' ? carrier.__gevContextId : null; + const contextId = readEntityRecord(carrier)?.contextId; if (!contextId) continue; const record = store.entities.get(contextId); if (record && isContextRecordActive(record, dataManager)) return record; diff --git a/src/layers/localAdsb/index.test.mjs b/src/layers/localAdsb/index.test.mjs index 11596d603..566433950 100644 --- a/src/layers/localAdsb/index.test.mjs +++ b/src/layers/localAdsb/index.test.mjs @@ -12,7 +12,7 @@ import { localAdsbReceiverLine, localAdsbStatus, } from './index.js'; -import { readEntityRecord } from '../../data/entityRecords.js'; +import { entityRecord, readEntityRecord } from '../../data/entityRecords.js'; function fakeReceiver(initial = {}) { const state = { @@ -56,11 +56,11 @@ function fakeServices() { render: { governorRequestRender() {} }, context: { registerEntityContext(entity, metadata) { - entity.__gevContextId = metadata.id; + entityRecord(entity).contextId = metadata.id; calls.registered.push(metadata); }, selectEntityContext(entity) { - selected = { id: entity.__gevContextId, entity }; + selected = { id: readEntityRecord(entity)?.contextId, entity }; calls.selected.push(selected.id); return selected; }, diff --git a/src/voice/gevActions.test.mjs b/src/voice/gevActions.test.mjs index 96a24edc6..cc7602cbe 100644 --- a/src/voice/gevActions.test.mjs +++ b/src/voice/gevActions.test.mjs @@ -1303,7 +1303,7 @@ test('control_cockpit delegates selected-flight adoption to the canonical cockpi scene: { canvas: { addEventListener() {}, removeEventListener() {} } }, camera: { moveEnd: { addEventListener() {} } }, }; - const recordCarrier = { __gevContextId: 'selected-flight-for-cockpit-enter' }; + const recordCarrier = {}; const record = registerEntityContext(recordCarrier, { id: 'abc123', layerId: 'flights', @@ -1444,10 +1444,10 @@ test('control_cockpit rolls Contacts back when the turn becomes stale at commit' test('control_cockpit adopts the newest selection after Contacts settles', async () => { globalThis.window = globalThis.window || { clearTimeout, setTimeout, requestIdleCallback: null }; const contextStore = getContextStore(); - const first = registerEntityContext({ __gevContextId: 'cockpit-selection-first' }, { + const first = registerEntityContext({}, { id: 'first', layerId: 'flights', label: 'FIRST', }); - const second = registerEntityContext({ __gevContextId: 'cockpit-selection-second' }, { + const second = registerEntityContext({}, { id: 'second', layerId: 'flights', label: 'SECOND', }); contextStore.selectedEntityId = first.id; @@ -1713,7 +1713,7 @@ test('control_cockpit enter skips selected non-flight context', async () => { scene: { canvas: { addEventListener() {}, removeEventListener() {} } }, camera: { moveEnd: { addEventListener() {} } }, }; - const recordCarrier = { __gevContextId: 'selected-non-flight-cockpit-enter' }; + const recordCarrier = {}; const record = registerEntityContext(recordCarrier, { id: 'poi-001', layerId: 'local-datacenters',