Merge branch 'main' into feat/voice-geometry

# Conflicts:
#	SECURITY.md
#	scripts/format-scope.json
#	src/app/constructCatalog.test.mjs
#	src/app/sourceComposition.test.mjs
#	src/firstRunExperience.test.mjs
#	src/i18n/locales/ar.json
#	src/i18n/locales/de.json
#	src/i18n/locales/es.json
#	src/i18n/locales/fr.json
#	src/i18n/locales/it.json
#	src/i18n/locales/ja.json
#	src/i18n/locales/pt.json
#	src/i18n/locales/ru.json
#	src/i18n/locales/uk.json
#	src/i18n/locales/zh-CN.json
#	src/i18n/locales/zh-Hant.json
#	src/voice/actionSchemas.test.mjs
This commit is contained in:
Sameh Khamis committed 2026-11-03 09:07:16 -08:00
commit 51637a5e96
56 files changed
+1834 -208

No files matched your search

+12 -1
View File
@@ -40,9 +40,11 @@ export function normalizeOpenSkyAircraft(row) {
};
}
const readsbId = (row) => cleanText(row?.hex).toLowerCase();
/** Normalize readsb observations. The caller supplies the source snapshot epoch. */
export function normalizeReadsbAircraft(row, snapshotTimeMs) {
const id = cleanText(row?.hex).toLowerCase();
const id = readsbId(row);
const latitude = finite(row?.lat),
longitude = finite(row?.lon);
if (!id || !coordinates(latitude, longitude)) return null;
@@ -129,6 +131,14 @@ export function openSkySnapshot(
};
}
/**
* readsb omits lat/lon for a contact heard without a current position. Both
* must be absent; a half or unreadable position still marks the row malformed.
*/
function readsbUnpositionedId(row) {
return row?.lat == null && row?.lon == null ? readsbId(row) : '';
}
export function readsbSnapshot(
payload,
{
@@ -143,6 +153,7 @@ export function readsbSnapshot(
payload?.ac,
(row) => normalizeReadsbAircraft(row, observedAtMs),
'adsb.lol',
readsbUnpositionedId,
);
const ageMs = observedAtMs == null ? null : Math.max(0, now - observedAtMs);
return {
+30 -8
View File
@@ -102,13 +102,34 @@ test('position-less OpenSky rows are seen, not missing; malformed rows stay inco
assert.throws(() => openSkySnapshot({ states: [unpositioned] }), {
code: 'malformed',
});
// Military readsb admission keeps its existing semantics.
const military = readsbSnapshot(
{ ac: [{ hex: 'abc123', lat: 30, lon: -97 }, { hex: 'def456' }] },
{ observedAtMs: null },
);
assert.equal(military.complete, false);
assert.equal(military.retainIds.size, 0);
});
test('position-less readsb rows are seen, not missing; malformed rows stay incomplete', () => {
// readsb omits lat/lon for a contact heard without a current position; a
// live adsb.lol /v2/mil fetch had 82 such rows of 239.
const positioned = { hex: 'ae638d', lat: 20.3, lon: -158.5, seen_pos: 0.2 };
const heard = { hex: 'ADFAC3', type: 'mode_s', rr_lat: 19.9, seen: 24.2 };
const lastPosition = {
hex: '3b752d',
lastPosition: { lat: 45.3, lon: 5.98, seen_pos: 61.56 },
};
const read = (ac) => readsbSnapshot({ ac }, { observedAtMs: null });
const snapshot = read([positioned, heard, lastPosition]);
assert.equal(snapshot.records.length, 1);
assert.equal(snapshot.complete, true);
assert.deepEqual([...snapshot.retainIds], ['adfac3', '3b752d']);
assert.equal(snapshot.rejectedCount, 2);
// No usable id, only half a position, an unreadable one, or no row at all.
for (const row of [
{ ...heard, hex: ' ' },
{ ...heard, lat: 30 },
{ ...heard, lon: -97 },
{ ...heard, lat: 'x', lon: 'y' },
null,
])
assert.equal(read([positioned, heard, row]).complete, false);
// A feed with no positions at all still cannot prove absence.
assert.throws(() => read([heard]), { code: 'malformed' });
});
test('readsb units and position/contact ages are normalized once against the source epoch', () => {
@@ -346,7 +367,8 @@ test('classification identities include positionless aircraft without admitting
snapshot.records.map((record) => record.id),
['def456'],
);
assert.equal(snapshot.complete, false);
assert.equal(snapshot.complete, true);
assert.deepEqual([...snapshot.retainIds], ['abc123']);
});
test('identity lookup validates its response and honors body-parse cancellation', async () => {