Merge remote-tracking branch 'origin/main' into german/map-render-error-state

# Conflicts:
#	src/app/scene.js
#	src/app/viewer.js
#	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/ui/views/SceneChromeView.vue
This commit is contained in:
German Khokhlov committed 2026-11-10 14:29:34 +04:00
commit 2448662574
608 files changed
+105160 -2242

No files matched your search

+57 -1
View File
@@ -7,13 +7,16 @@
* Natural Earth 10m admin-1 states and provinces (public domain).
* src/data/local_data/us_census_counties/counties.json
* US Census Bureau cartographic boundary counties (public domain).
* src/data/local_data/natural_earth/populated_places.json
* Natural Earth 10m populated places as points, for the orbital view's
* "near <city>" (`src/data/placeBelow.js`; public domain).
*
* Both sources are pinned by URL and SHA-256, so a rerun reproduces the packs
* byte for byte. Downloads are cached outside the repository (default: the
* system temp directory) and are never committed.
*
* Usage:
* node scripts/build-admin-packs.mjs [--cache <dir>] [--only countries|admin1|counties]
* node scripts/build-admin-packs.mjs [--cache <dir>] [--only countries|admin1|counties|places]
*
* The provenance READMEs beside each pack record these parameters.
*/
@@ -101,6 +104,9 @@ export const PARAMS = Object.freeze({
elsewhereMinPopulation: 50_000,
cityStateMaxKm2: 1_000,
},
// Every populated place, kept as a point: name, position and the two
// prominence ranks the orbital view picks "near <city>" by.
places: { decimals: 3 },
});
const args = process.argv.slice(2);
@@ -688,6 +694,47 @@ async function buildAdmin1() {
};
}
async function buildPlaces() {
const params = PARAMS.places;
const source = await fetchPinned('places');
const factor = 10 ** params.decimals;
const round = (value) => Math.round(value * factor) / factor;
const places = JSON.parse(source.bytes.toString('utf8'))
.features.map((f) => f.properties)
.filter(
(p) =>
p.name &&
Number.isFinite(p.latitude) &&
Number.isFinite(p.longitude) &&
Number.isFinite(p.scalerank),
)
.map((p) => [
p.name,
round(p.latitude),
round(p.longitude),
p.scalerank,
Number.isFinite(p.rank_max) ? p.rank_max : 0,
])
.sort((a, b) => a[3] - b[3] || b[4] - a[4] || a[0].localeCompare(b[0]));
return {
meta: {
title: 'Natural Earth populated places',
source: 'ne_10m_populated_places_simple',
url: SOURCES.places.url,
sha256: source.digest,
commit: NE_COMMIT,
license:
'Public domain (https://www.naturalearthdata.com/about/terms-of-use/)',
schema:
'places[]: [name, latitude, longitude, scalerank (0 most prominent .. 10), rank_max (population class, 0..14; higher is larger)]',
decimals: params.decimals,
curation: params,
script: 'scripts/build-admin-packs.mjs',
},
places,
};
}
async function buildCounties() {
const params = PARAMS.counties;
const source = await fetchPinned('counties');
@@ -768,4 +815,13 @@ if (isMain) {
'src/data/local_data/us_census_counties/counties.json',
await buildCounties(),
);
if (!only || only === 'places') {
const relative = 'src/data/local_data/natural_earth/populated_places.json';
const pack = await buildPlaces();
const text = JSON.stringify(pack) + '\n';
await writeFile(path.join(ROOT, relative), text);
console.log(
`${relative}: ${pack.places.length} places, ${Buffer.byteLength(text)} bytes`,
);
}
}
+101
View File
@@ -0,0 +1,101 @@
#!/usr/bin/env node
/**
* Build the owner flag sheet the orbital Cockpit marks a satellite's owner
* country with (`src/ui/ownerFlag.js`): every flag in OWNER_COUNTRY_CODES
* (`src/layers/satellites/classes.js`), drawn from the flag-icons 4:3 SVGs
* at twice the readout's 20 × 15 px, in one PNG the app loads at startup.
*
* src/data/local_data/owner_flags/owner_flags.png the sheet
* src/ui/ownerFlagSheet.js its layout (generated)
*
* Flag i (in the sorted code list) sits at column i % COLUMNS, row
* floor(i / COLUMNS), each cell followed by a GUTTER of transparent pixels. A rerun with the same flag-icons and sharp versions
* reproduces both files.
*
* Usage: node scripts/build-owner-flags.mjs
*/
import { readFile, writeFile } from 'node:fs/promises';
import { createRequire } from 'node:module';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import * as prettier from 'prettier';
import sharp from 'sharp';
import { OWNER_COUNTRY_CODES } from '../src/layers/satellites/classes.js';
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const require = createRequire(import.meta.url);
const FLAGS_DIR = path.join(
path.dirname(require.resolve('flag-icons/package.json')),
'flags/4x3',
);
const FLAG_ICONS_VERSION = require('flag-icons/package.json').version;
/** One flag's cell, CSS pixels × 2. */
export const CELL = Object.freeze({ width: 40, height: 30 });
/**
* Transparent sheet pixels after each flag, so a browser smoothing a scaled
* flag (a 125% display) never reaches into its neighbour.
*/
export const GUTTER = 2;
export const COLUMNS = 16;
const SHEET = path.join(
ROOT,
'src/data/local_data/owner_flags/owner_flags.png',
);
const LAYOUT = path.join(ROOT, 'src/ui/ownerFlagSheet.js');
async function flagCell(code) {
const svg = await readFile(path.join(FLAGS_DIR, `${code.toLowerCase()}.svg`));
return sharp(svg, { density: 300 })
.resize(CELL.width, CELL.height, { fit: 'fill' })
.png()
.toBuffer();
}
const codes = [...OWNER_COUNTRY_CODES];
const rows = Math.ceil(codes.length / COLUMNS);
const pitchX = CELL.width + GUTTER;
const pitchY = CELL.height + GUTTER;
const cells = await Promise.all(codes.map(flagCell));
const sheet = await sharp({
create: {
width: COLUMNS * pitchX,
height: rows * pitchY,
channels: 4,
background: { r: 0, g: 0, b: 0, alpha: 0 },
},
})
.composite(
cells.map((input, i) => ({
input,
left: (i % COLUMNS) * pitchX,
top: Math.floor(i / COLUMNS) * pitchY,
})),
)
.png({ compressionLevel: 9, palette: true, quality: 100, effort: 10 })
.toBuffer();
await writeFile(SHEET, sheet);
const layout = `// Generated by scripts/build-owner-flags.mjs from flag-icons ${FLAG_ICONS_VERSION}; do not edit.
/** The owner flag sheet's layout: flag i at column i % columns, row i / columns. */
export const OWNER_FLAG_SHEET = Object.freeze({
cellWidthPx: ${CELL.width},
cellHeightPx: ${CELL.height},
gutterPx: ${GUTTER},
columns: ${COLUMNS},
rows: ${rows},
codes: Object.freeze(${JSON.stringify(codes)}),
});
`;
await writeFile(
LAYOUT,
await prettier.format(layout, {
...(await prettier.resolveConfig(LAYOUT)),
filepath: LAYOUT,
}),
);
console.log(
`Wrote ${codes.length} flags (${COLUMNS} × ${rows}), ${(sheet.length / 1024).toFixed(1)} KB.`,
);
+118
View File
@@ -0,0 +1,118 @@
#!/usr/bin/env python3
'''Maintainer-only UCS Satellite Database pack build (openpyxl).
Reads the UCS Excel release and writes the compact pack the satellites layer
loads: one row per satellite, keyed by NORAD number, with string-table indexes
for the repeated text columns. Starlink rows are left out: the layer files
every STARLINK object under its own class and operator without them.
Usage: python scripts/build-ucs-satellites.py "<path>/UCS-Satellite-Database 5-1-2023.xlsx"
'''
import argparse
import json
import logging
from pathlib import Path
import re
import openpyxl
LOG = logging.getLogger('build-ucs-satellites')
ROOT = Path(__file__).resolve().parent.parent
PACK = ROOT / 'src/data/local_data/ucs_satellites'
VERSION = 'UCS-Satellite-Database 5-1-2023'
DATA_CURRENT_TO = '2023-05-01'
URL = 'https://www.ucsusa.org/satellite_database'
# Output column -> spreadsheet header.
COLUMNS = {
'operator': 'Operator/Owner',
'operatorCountry': 'Country of Operator/Owner',
'purpose': 'Purpose',
}
STARLINK = re.compile(r'\bstarlink\b', re.I)
def clean(value: object) -> str:
'''One cell as tidy text; UCS's placeholders for nothing become ''.'''
text = re.sub(r'\s+', ' ', str(value or '')).strip()
return '' if text.lower() in ('', 'none', 'n/a') else text
def build(xlsx: Path) -> dict:
'''Read the release's first sheet into the pack the layer loads.'''
sheet = openpyxl.load_workbook(xlsx, read_only=True, data_only=True).worksheets[0]
rows = sheet.iter_rows(values_only=True)
header = {str(h).strip(): i for i, h in enumerate(next(rows)) if h}
names = [header['Current Official Name of Satellite'], header['Name of Satellite, Alternate Names']]
norad_col = header['NORAD Number']
tables = {key: [''] for key in COLUMNS}
index = {key: {'': 0} for key in COLUMNS}
records = {}
source_rows = skipped_starlink = invalid_norad = 0
for row in rows:
if not any(row):
continue
source_rows += 1
if any(STARLINK.search(clean(row[i])) for i in names):
skipped_starlink += 1
continue
try:
norad = int(float(row[norad_col]))
except (TypeError, ValueError):
invalid_norad += 1
continue
if norad <= 0:
invalid_norad += 1
continue
record = [norad]
for key, column in COLUMNS.items():
text = clean(row[header[column]])
if text not in index[key]:
index[key][text] = len(tables[key])
tables[key].append(text)
record.append(index[key][text])
# A duplicate NORAD number keeps the first row, as UCS lists it.
records.setdefault(norad, record)
ordered = [records[n] for n in sorted(records)]
return {
'meta': {
'source': 'Union of Concerned Scientists Satellite Database',
'version': VERSION,
'url': URL,
'dataCurrentTo': DATA_CURRENT_TO,
'license': 'Free, unrestricted use; acknowledgement requested (UCS database manual, section 3)',
'curation': {
'sourceRows': source_rows,
'starlinkRowsDropped': skipped_starlink,
'invalidNoradDropped': invalid_norad,
'duplicateNoradDropped': source_rows - skipped_starlink - invalid_norad - len(ordered),
'columns': COLUMNS,
},
'recordCount': len(ordered),
},
'fields': ['noradId', *COLUMNS],
'strings': tables,
'records': ordered,
}
def main() -> None:
'''Build the pack from the release given on the command line.'''
logging.basicConfig(level=logging.INFO, format='%(message)s')
parser = argparse.ArgumentParser(description='Build the UCS satellite pack from the UCS Excel release.')
parser.add_argument('xlsx', type=Path, help='UCS-Satellite-Database <version>.xlsx')
pack = build(parser.parse_args().xlsx)
PACK.mkdir(parents=True, exist_ok=True)
out = PACK / 'ucs_satellites.json'
# LF on every platform: the repository stores text with LF endings.
out.write_text(
json.dumps(pack, ensure_ascii=False, separators=(',', ':')) + '\n',
encoding='utf-8',
newline='\n',
)
LOG.info('%s: %d records, %d bytes', out.relative_to(ROOT), pack['meta']['recordCount'], out.stat().st_size)
if __name__ == '__main__':
main()
+1 -1
View File
@@ -44,7 +44,7 @@ CCTV_NSW_ENABLED="${CCTV_NSW_ENABLED:-1}"
CCTV_NSW_MAX_SOURCES="${CCTV_NSW_MAX_SOURCES:-250}"
CCTV_CALGARY_ENABLED="${CCTV_CALGARY_ENABLED:-1}"
CCTV_CALGARY_MAX_SOURCES="${CCTV_CALGARY_MAX_SOURCES:-220}"
CCTV_MAX_SOURCES="${CCTV_MAX_SOURCES:-4000}"
CCTV_MAX_SOURCES="${CCTV_MAX_SOURCES:-7350}"
# Capture which provider credentials genuinely came from the parent shell
# before this launcher resolves dotenv and Keychain fallbacks. Only names are
+106 -1
View File
@@ -112,6 +112,8 @@
"src/data/terrainHeightsProxy.test.mjs",
"src/data/tomtomTiles.js",
"src/data/tomtomTiles.test.mjs",
"src/data/tileBudget.js",
"src/data/tileBudget.test.mjs",
"src/data/firmsCsv.js",
"src/data/firmsCsv.test.mjs",
"src/tooling/environmentProviders.test.mjs",
@@ -328,6 +330,12 @@
"src/ui/styles/responsive.css",
"src/ui/styles/voice-cost.css",
"src/ui/styles/cockpit.css",
"src/ui/styles/cockpitBridge.css",
"src/ui/styles/cockpitRadar.css",
"src/ui/styles/cockpitOrbital.css",
"src/layers/satellites/sensors.js",
"src/layers/satellites/sensors.test.mjs",
"src/data/imagingSensors.js",
"src/ui/styles/command-dock-compact.css",
"src/app/stateChannel.js",
"src/app/stateChannel.test.mjs",
@@ -347,6 +355,7 @@
"src/layers/flights/tracking.js",
"src/layers/flights/rendering.js",
"src/layers/flights/testing.js",
"src/layers/flights/hiddenContactRotation.test.mjs",
"src/layers/flights/evidence.js",
"src/layers/flights/ingestion.js",
"src/layers/flights/queries.js",
@@ -474,6 +483,7 @@
"src/layers/satellites/stale.test.mjs",
"src/layers/launches/source.test.mjs",
"src/layers/launches/ingestion.test.mjs",
"src/layers/launches/failedEntry.test.mjs",
"src/layers/installations/lifecycle.js",
"src/layers/installations/viewport.js",
"src/layers/installations/controls.js",
@@ -586,6 +596,7 @@
"src/data/cctvHk.test.mjs",
"src/data/cctvVegvesen.test.mjs",
"src/data/cctvA22.test.mjs",
"src/data/cctvIceland.test.mjs",
"src/data/cctvNyc.test.mjs",
"src/data/cctvHouston.test.mjs",
"src/data/cctvNswSource.test.mjs",
@@ -791,6 +802,7 @@
"server/providers/transit.js",
"server/providers/transitHistory.js",
"server/providers/transitHistory.test.mjs",
"src/data/rioRouteNames.js",
"src/data/transitFeeds.js",
"src/data/transitFeeds.test.mjs",
"src/data/transitProxy.js",
@@ -1070,6 +1082,7 @@
"src/layers/traffic/roadModes.test.mjs",
"scripts/qa-traffic-motion.mjs",
"src/layers/traffic/motion.test.mjs",
"src/layers/traffic/lineRebuild.test.mjs",
"scripts/qa-journey-recorder.mjs",
"scripts/qa-alpr-journey.mjs",
"scripts/qa-context-arrivals.mjs",
@@ -1121,6 +1134,7 @@
"src/sky/starrySky.test.mjs",
"src/tooling/skyAssets.test.mjs",
"src/tooling/skyAssets.paintOut.test.mjs",
"src/tooling/responseCompression.test.mjs",
"scripts/qa-anime-sky.mjs",
"scripts/qa-sky-model-lighting.mjs",
"server/providers/overpass/requestAdmission.test.mjs",
@@ -1131,6 +1145,8 @@
"scripts/qa-status-badges.mjs",
"src/dockerTrustedPeers.test.mjs",
"src/cameraGlobeRotation.test.mjs",
"src/cameraTilt.test.mjs",
"src/ui/shellFacade.test.mjs",
"src/voice/globeResetConfirmation.test.mjs",
"src/hudDms.test.mjs",
"src/hudSummaryGate.test.mjs",
@@ -1272,5 +1288,94 @@
"src/voice/geminiTextChat.js",
"src/voice/panelLayout.js",
"src/voice/textChat.js",
"src/voice/textProtocol.js"
"src/voice/textProtocol.js",
"src/app/sourceComposition.js",
"src/app/sourceComposition.test.mjs",
"src/data/dataCredits.js",
"src/data/layerState.js",
"src/data/layerState.test.mjs",
"src/data/spriteOrder.js",
"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/i18n/same-as-english.json",
"src/navigationPolicy.js",
"src/overlays/worldOverlayDraw.js",
"src/overlays/worldOverlayDraw.test.mjs",
"src/sources/rateLimit.js",
"src/ui/layerNames.js",
"src/ui/styles/cyber.css",
"src/voice/layerManifest.js",
"src/worldFocus.js",
"src/worldFocus.test.mjs",
"server/providers/acartia.js",
"server/providers/acartia.test.mjs",
"server/providers/common/userAgent.js",
"server/providers/inaturalist/client.js",
"server/providers/inaturalist/observations.js",
"server/providers/inaturalist/observations.test.mjs",
"src/app/layers/wildlifeSightings.js",
"src/layers/wildlifeSightings/cards.js",
"src/layers/wildlifeSightings/cards.test.mjs",
"src/layers/wildlifeSightings/controls.js",
"src/layers/wildlifeSightings/docs.test.mjs",
"src/layers/wildlifeSightings/index.js",
"src/layers/wildlifeSightings/lifecycle.js",
"src/layers/wildlifeSightings/model.js",
"src/layers/wildlifeSightings/model.test.mjs",
"src/layers/wildlifeSightings/ownership.test.mjs",
"src/layers/wildlifeSightings/photos.js",
"src/layers/wildlifeSightings/photos.test.mjs",
"src/layers/wildlifeSightings/policy.js",
"src/layers/wildlifeSightings/records.js",
"src/layers/wildlifeSightings/records.test.mjs",
"src/layers/wildlifeSightings/rendering.js",
"src/layers/wildlifeSightings/selection.js",
"src/layers/wildlifeSightings/source.js",
"src/layers/wildlifeSightings/source.test.mjs",
"src/layers/wildlifeSightings/viewport.js",
"src/layers/wildlifeSightings/viewport.test.mjs",
"src/sources/rateLimit.test.mjs",
"src/layers/wildlifeSightings/bounds.js",
"src/layers/wildlifeSightings/acartiaTaxa.js",
"src/layers/wildlifeSightings/acquisition.test.mjs",
"server/providers/inaturalist/client.test.mjs",
"src/layers/wildlifeSightings/acartiaTaxa.test.mjs",
"src/ui/wildlifeSightingCard.test.mjs",
"src/overlays/wildlifeSightingOverlay.test.mjs",
"scripts/run-perf-tests.mjs",
"src/perfTestRunner.test.mjs",
"src/testSupport/wildlifeBillboardModel.mjs",
"src/layers/wildlifeSightings/gridViewport.test.mjs",
"src/layers/wildlifeSightings/rendering.test.mjs",
"src/layers/wildlifeSightings/sprites.test.mjs",
"src/layers/wildlifeSightings/performance.test.mjs",
"src/layers/wildlifeSightings/model.perf.test.mjs",
"src/ui/wildlifeSightingPlacement.js",
"server/providers/inaturalist/queue.js",
"server/providers/inaturalist/queue.test.mjs",
"server/providers/inaturalist/grid.js",
"server/providers/inaturalist/grid.test.mjs",
"server/providers/inaturalist/grid.fixture.mjs",
"src/layers/wildlifeSightings/performance.js",
"src/layers/wildlifeSightings/renderPerformanceFixtures.mjs",
"server/providers/inaturalist/speed.test.mjs",
"src/layers/wildlifeSightings/render.perf.test.mjs",
"src/layers/wildlifeSightings/cache.integration.test.mjs",
"src/layers/wildlifeSightings/density.test.mjs",
"src/layers/wildlifeSightings/selection.test.mjs",
"server/providers/inaturalist/proxy.fixture.mjs",
"scripts/build-owner-flags.mjs",
"src/ui/ownerFlagSheet.js",
"src/data/placePacksLoading.test.mjs",
"src/ui/placePacksLoading.js",
"src/ui/placePacksLoading.test.mjs"
]
+362 -20
View File
@@ -39,6 +39,13 @@
"src/ui/views/panels/CockpitBriefing.vue",
"src/ui/views/panels/CockpitContext.vue",
"src/ui/views/panels/CockpitRoute.vue",
"src/ui/views/panels/CockpitRadar.vue",
"src/ui/views/panels/CockpitOrbital.vue",
"src/ui/ownerFlag.js",
"src/ui/ownerFlagSheet.js",
"src/ui/cockpitPresentation.js",
"src/data/headingDegrees.js",
"src/data/distanceFormat.js",
"src/ui/views/panels/CockpitRollingValue.vue",
"src/ui/views/panels/CockpitSignalList.vue",
"src/ui/views/panels/CockpitUtilityControls.vue",
@@ -152,15 +159,26 @@
"src/ui/views/panels/GlobeCompass.vue",
"src/ui/stores/recording.js",
"src/ui/styleParameters.js",
"src/ui/i18n.js"
"src/ui/i18n.js",
"src/time/worldClock.js",
"src/ui/kit/UiIcon.vue",
"src/ui/stores/worldTime.js",
"src/ui/views/panels/WorldTimeline.vue",
"src/ui/surfaceKeyboard.js"
],
"external": ["vue", "pinia", "vue-i18n"]
},
"time": {
"exports": ["./time"],
"modules": ["src/time/worldClock.js"],
"external": []
},
"viewer": {
"exports": ["./application/viewer"],
"modules": [
"src/app/viewer.js",
"src/app/atmosphereCompat.js",
"src/globeInView.js",
"src/sky/starrySky.js",
"src/sky/stars.js",
"src/data/bundledJson.js",
@@ -188,6 +206,7 @@
"modules": [
"src/sources/adsbFields.js",
"server/providers/live.js",
"server/providers/common/compress.js",
"server/providers/common/http.js",
"server/providers/common/query.js",
"server/providers/aircraft/opensky.js",
@@ -200,7 +219,22 @@
"src/data/aisStreamAdapter.js",
"src/data/aisWatchdog.js",
"src/sources/httpBody.js",
"src/i18n/marker.js"
"src/i18n/marker.js",
"server/providers/acartia.js",
"server/providers/common/rate-limit.js",
"server/providers/common/same-site.js",
"server/providers/common/userAgent.js",
"server/providers/inaturalist/client.js",
"server/providers/inaturalist/observations.js",
"src/layers/wildlifeSightings/records.js",
"src/layers/wildlifeSightings/policy.js",
"src/localRequestGate.mjs",
"src/sources/rateLimit.js",
"src/layers/wildlifeSightings/bounds.js",
"src/layers/wildlifeSightings/acartiaTaxa.js",
"server/providers/inaturalist/grid.js",
"src/layers/wildlifeSightings/gridViewport.js",
"server/providers/inaturalist/queue.js"
],
"external": ["ws"]
},
@@ -209,6 +243,11 @@
"modules": ["src/data/adsbLolFallback.js", "src/sources/adsbFields.js"],
"external": []
},
"planespotters-source": {
"exports": ["./sources/planespotters"],
"modules": ["src/data/planespottersPhoto.js"],
"external": []
},
"place-providers": {
"runtime": "node",
"exports": ["./server/providers/places"],
@@ -286,6 +325,7 @@
"src/sources/httpBody.js",
"src/i18n/marker.js",
"src/data/tomtomTiles.js",
"src/data/tileBudget.js",
"server/providers/common/same-site.js",
"src/localRequestGate.mjs"
],
@@ -293,7 +333,7 @@
},
"traffic-source": {
"exports": ["./sources/traffic"],
"modules": ["src/data/tomtomTiles.js"],
"modules": ["src/data/tomtomTiles.js", "src/data/tileBudget.js"],
"external": []
},
"firms-csv": {
@@ -344,6 +384,9 @@
"server/providers/cctv/catalog.js",
"server/providers/cctv/lithuania.js",
"server/providers/cctv/taiwan.js",
"server/providers/cctv/quebec.js",
"server/providers/cctv/ottawa.js",
"server/providers/cctv/worldCameras.js",
"server/providers/cctv/constants.js",
"server/providers/cctv/media.js",
"server/providers/cctv/normalize.js",
@@ -553,6 +596,7 @@
"src/search/placeSearch.js",
"src/search/google.js",
"src/keylessGeocoder.js",
"src/data/placeNames.js",
"src/search/geospatial.js",
"src/search/http.js",
"src/search/defaults.js",
@@ -786,6 +830,7 @@
"cctv-controls": {
"exports": ["./ui/cctv"],
"modules": [
"src/data/iconOrientation.js",
"src/data/headingDegrees.js",
"src/ui/cctv.js",
"src/ui/cctvBindings.js",
@@ -794,6 +839,7 @@
"src/ui/cctvFrames.js",
"src/ui/cctvVideo.js",
"src/ui/cctvPresentation.js",
"src/ui/quebecCctvOverlay.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/ar.json",
@@ -813,7 +859,7 @@
"src/ui/stores/status.js",
"src/ui/i18n.js"
],
"external": ["vue", "pinia", "vue-i18n"]
"external": ["vue", "pinia", "vue-i18n", "cesium"]
},
"context-controls": {
"exports": ["./ui/context", "./ui/context/policy"],
@@ -865,11 +911,25 @@
"src/ui/cockpitInstrumentState.js",
"src/ui/cockpitRollingValue.js",
"src/ui/cockpitCamera.js",
"src/ui/cockpitOrbitalView.js",
"src/ui/cockpitOrbitalFocus.js",
"src/ui/cockpitOrbitalSignals.js",
"src/ui/cockpitOrbitalSatellites.js",
"src/cockpitOrbitalSatellites.js",
"src/layers/installations/classLabel.js",
"src/data/installationNames.js",
"src/i18n/marker.js",
"src/data/placeBelow.js",
"src/layers/satellites/sensors.js",
"src/data/adminBoundaries.js",
"src/data/bundledJson.js",
"src/data/retryableLoad.js",
"src/ui/cockpitContext.js",
"src/ui/cockpitController.js",
"src/ui/cockpitDisplayPortal.js",
"src/ui/displayPanelScroll.js",
"src/ui/cockpitInput.js",
"src/data/inputOwnership.js",
"src/ui/cockpitInstruments.js",
"src/ui/cockpitLayout.js",
"src/ui/cockpitPresentation.js",
@@ -878,6 +938,13 @@
"src/cockpitMath.js",
"src/cockpitUtilityLayout.js",
"src/cockpitVisionPolicy.js",
"src/cockpitSubjects.js",
"src/cockpitRadar.js",
"src/cockpitLook.js",
"src/cockpitOrbital.js",
"src/cockpitOrbitalFocus.js",
"src/geoDistance.js",
"src/data/motionModel.js",
"src/ui/panelRailGeometry.js",
"src/i18n/index.js",
"src/i18n/locale.js",
@@ -892,6 +959,7 @@
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
"src/ui/layerNames.js",
"src/ui/unitLabels.js"
],
"external": ["cesium"]
@@ -945,6 +1013,13 @@
"src/cockpitTracking.js",
"src/cockpitUtilityLayout.js",
"src/cockpitVisionPolicy.js",
"src/cockpitSubjects.js",
"src/cockpitRadar.js",
"src/cockpitLook.js",
"src/cockpitOrbital.js",
"src/cockpitOrbitalFocus.js",
"src/geoDistance.js",
"src/data/motionModel.js",
"src/contactsDetectionPolicy.js",
"src/contextModePolicy.js",
"src/data/detectionPolicy.js",
@@ -952,6 +1027,7 @@
"src/data/idSwitches.js",
"src/data/firmsIntensity.js",
"src/data/layerState.js",
"src/data/satelliteClassMask.js",
"src/data/layerStateCoordinator.js",
"src/data/layerStateTokenReservations.json",
"src/data/militaryAwarenessEngine.js",
@@ -984,6 +1060,8 @@
"src/ui/cctvFrames.js",
"src/ui/cctvVideo.js",
"src/ui/cctvPresentation.js",
"src/data/iconOrientation.js",
"src/ui/quebecCctvOverlay.js",
"src/ui/clearLayersControl.js",
"src/ui/cockpit.js",
"src/ui/cockpitBriefing.js",
@@ -992,11 +1070,26 @@
"src/ui/cockpitInstrumentState.js",
"src/ui/cockpitRollingValue.js",
"src/ui/cockpitCamera.js",
"src/ui/cockpitOrbitalView.js",
"src/ui/cockpitOrbitalFocus.js",
"src/ui/cockpitOrbitalSignals.js",
"src/ui/cockpitOrbitalSatellites.js",
"src/cockpitOrbitalSatellites.js",
"src/layers/installations/classLabel.js",
"src/data/placeBelow.js",
"src/ui/placePacksLoading.js",
"src/ui/ownerFlag.js",
"src/ui/ownerFlagSheet.js",
"src/layers/satellites/sensors.js",
"src/data/adminBoundaries.js",
"src/data/bundledJson.js",
"src/data/retryableLoad.js",
"src/ui/cockpitContext.js",
"src/ui/cockpitController.js",
"src/ui/cockpitDisplayPortal.js",
"src/ui/displayPanelScroll.js",
"src/ui/cockpitInput.js",
"src/data/inputOwnership.js",
"src/ui/cockpitInstruments.js",
"src/ui/cockpitLayout.js",
"src/ui/cockpitPresentation.js",
@@ -1149,7 +1242,15 @@
"src/ui/stores/globeActions.js",
"src/ui/stores/recording.js",
"src/ui/i18n.js",
"src/ui/kit/UiSegmented.vue"
"src/time/worldClock.js",
"src/ui/kit/UiSegmented.vue",
"src/ui/wildlifeSightingCard.js",
"src/layers/wildlifeSightings/records.js",
"src/layers/wildlifeSightings/acartiaTaxa.js",
"src/ui/views/panels/WildlifeSightingCard.vue",
"src/ui/wildlifeSightingPlacement.js",
"src/overlays/worldOverlayChrome.js",
"src/layers/wildlifeSightings/photoText.js"
],
"external": ["cesium", "vue", "pinia", "vue-i18n"]
},
@@ -1163,10 +1264,13 @@
"src/ui/styles/location.css",
"src/ui/styles/status.css",
"src/ui/styles/cockpit.css",
"src/ui/styles/cockpitBridge.css",
"src/ui/styles/cockpitRadar.css",
"src/ui/styles/overlays.css",
"src/ui/styles/layers.css",
"src/ui/styles/radio.css",
"src/ui/styles/cctv.css",
"src/ui/styles/quebecCctvOverlay.css",
"src/ui/styles/scenes.css",
"src/ui/styles/bhote-koshi.css",
"src/ui/styles/recording.css",
@@ -1297,7 +1401,8 @@
"src/sources/live/contract.js",
"src/sources/live/index.js",
"src/sources/live/standalone.js",
"src/sources/live/vessels.js"
"src/sources/live/vessels.js",
"src/data/planespottersPhoto.js"
],
"external": []
},
@@ -1320,6 +1425,7 @@
"src/data/idSwitches.js",
"src/data/firmsIntensity.js",
"src/data/layerState.js",
"src/data/satelliteClassMask.js",
"src/data/layerStateTokenReservations.json",
"src/data/modelVisualAnchor.js",
"src/data/aircraftModelLighting.js",
@@ -1361,7 +1467,10 @@
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
"src/ui/unitLabels.js"
"src/ui/unitLabels.js",
"src/time/worldClock.js",
"src/time/julian.js",
"src/data/planespottersPhoto.js"
],
"external": ["cesium"]
},
@@ -1383,6 +1492,7 @@
"src/data/idSwitches.js",
"src/data/firmsIntensity.js",
"src/data/layerState.js",
"src/data/satelliteClassMask.js",
"src/data/layerStateTokenReservations.json",
"src/data/modelVisualAnchor.js",
"src/data/aircraftModelLighting.js",
@@ -1418,7 +1528,9 @@
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
"src/i18n/locales/zh-Hant.json",
"src/time/worldClock.js",
"src/time/julian.js"
],
"external": ["cesium"]
},
@@ -1437,6 +1549,7 @@
"src/data/idSwitches.js",
"src/data/firmsIntensity.js",
"src/data/layerState.js",
"src/data/satelliteClassMask.js",
"src/data/layerStateCoordinator.js",
"src/data/layerStateTokenReservations.json",
"src/data/modelVisualAnchor.js",
@@ -1477,6 +1590,8 @@
"src/layers/vessels/records.js",
"src/layers/vessels/recordPolicy.js",
"src/layers/vessels/snapshotRenderer.js",
"src/layers/vessels/cockpitSubject.js",
"src/geoDistance.js",
"src/data/inputOwnership.js",
"src/i18n/index.js",
"src/i18n/locale.js",
@@ -1576,9 +1691,11 @@
"exports": ["./layers/satellites"],
"modules": [
"src/data/satelliteClass.js",
"src/data/satelliteClassMask.js",
"src/data/satellitePass.js",
"src/sources/tle.js",
"src/layers/satellites/catalog.js",
"src/layers/satellites/cockpitSubject.js",
"src/layers/satellites/controls.js",
"src/layers/satellites/index.js",
"src/layers/satellites/ingestion.js",
@@ -1607,7 +1724,14 @@
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
"src/ui/unitLabels.js"
"src/ui/unitLabels.js",
"src/layers/satellites/classes.js",
"src/layers/satellites/sensors.js",
"src/data/ucsSatellites.js",
"src/data/imagingSensors.js",
"src/data/bundledJson.js",
"src/data/retryableLoad.js",
"src/time/worldClock.js"
],
"external": ["cesium", "satellite.js"]
},
@@ -1647,7 +1771,8 @@
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
"src/i18n/locales/zh-Hant.json",
"src/time/worldClock.js"
],
"external": ["cesium"]
},
@@ -1671,6 +1796,7 @@
"src/layers/installations/viewport.js",
"src/data/inputOwnership.js",
"src/data/tomtomTiles.js",
"src/data/tileBudget.js",
"src/sources/capability.js",
"src/sources/httpBody.js",
"src/i18n/marker.js",
@@ -1745,6 +1871,10 @@
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
"src/data/feedState.js",
"src/loadingFeedback.js",
"src/data/installationFeedback.js",
"src/ui/layerNames.js",
"src/ui/unitLabels.js"
],
"external": ["cesium"]
@@ -1755,6 +1885,7 @@
"src/data/cameraSensitivity.js",
"src/data/flowMatch.js",
"src/data/tomtomTiles.js",
"src/data/tileBudget.js",
"src/data/trafficBounds.js",
"src/geoDistance.js",
"src/data/trafficFlowStyle.js",
@@ -1968,7 +2099,8 @@
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
"src/layers/streetLevel/providers/mapillary/policy.js"
"src/layers/streetLevel/providers/mapillary/policy.js",
"src/time/worldClock.js"
],
"external": ["cesium", "pbf", "@mapbox/vector-tile", "mapillary-js"]
},
@@ -1986,6 +2118,7 @@
"src/data/inputOwnership.js",
"src/layers/alpr/records.js",
"src/data/tomtomTiles.js",
"src/data/tileBudget.js",
"src/layers/alpr/tileRecords.js",
"src/sources/capability.js",
"src/sources/httpBody.js",
@@ -2016,6 +2149,7 @@
"src/data/idSwitches.js",
"src/data/firmsIntensity.js",
"src/data/layerState.js",
"src/data/satelliteClassMask.js",
"src/data/layerStateTokenReservations.json",
"src/data/radioCountry.js",
"src/layers/radio/catalogModel.js",
@@ -2302,6 +2436,7 @@
"src/app/layers/alprCameras.js",
"src/app/layers/bikeshare.js",
"src/app/layers/cctv.js",
"src/app/layers/cyclones.js",
"src/app/layers/earthquakes.js",
"src/app/layers/firms.js",
"src/app/layers/flights.js",
@@ -2331,6 +2466,7 @@
"src/camera.js",
"src/cameraGroundGuard.js",
"src/cameraVerbs.js",
"src/globeInView.js",
"src/cctvFocusPolicy.js",
"src/cctvFocusRequest.js",
"src/celestialRing.js",
@@ -2339,6 +2475,11 @@
"src/cockpitTracking.js",
"src/cockpitUtilityLayout.js",
"src/cockpitVisionPolicy.js",
"src/cockpitSubjects.js",
"src/cockpitRadar.js",
"src/cockpitLook.js",
"src/cockpitOrbital.js",
"src/cockpitOrbitalFocus.js",
"src/contactsDetectionPolicy.js",
"src/contextModePolicy.js",
"src/creditKeyboard.js",
@@ -2403,6 +2544,7 @@
"src/data/labelArbiter.js",
"src/data/idSwitches.js",
"src/data/layerState.js",
"src/data/satelliteClassMask.js",
"src/data/layerStateCoordinator.js",
"src/data/layerStateTokenReservations.json",
"src/data/lifecycle.js",
@@ -2448,6 +2590,7 @@
"src/data/telegeographySubmarineCables.js",
"src/data/terrainHeights.js",
"src/data/tomtomTiles.js",
"src/data/tileBudget.js",
"src/data/tr3bRegistry.js",
"src/data/trackedCamera.js",
"src/data/trackedModelRegime.js",
@@ -2632,6 +2775,7 @@
"src/layers/radio/tuningNoise.js",
"src/layers/radio/volume.js",
"src/layers/satellites/catalog.js",
"src/layers/satellites/cockpitSubject.js",
"src/layers/satellites/controls.js",
"src/layers/satellites/index.js",
"src/layers/satellites/ingestion.js",
@@ -2772,6 +2916,7 @@
"src/ui/cctvFrames.js",
"src/ui/cctvVideo.js",
"src/ui/cctvPresentation.js",
"src/ui/quebecCctvOverlay.js",
"src/ui/clearLayersControl.js",
"src/ui/cockpit.js",
"src/ui/cockpitBriefing.js",
@@ -2780,6 +2925,15 @@
"src/ui/cockpitInstrumentState.js",
"src/ui/cockpitRollingValue.js",
"src/ui/cockpitCamera.js",
"src/ui/cockpitOrbitalView.js",
"src/ui/cockpitOrbitalFocus.js",
"src/ui/cockpitOrbitalSignals.js",
"src/ui/cockpitOrbitalSatellites.js",
"src/cockpitOrbitalSatellites.js",
"src/data/placeBelow.js",
"src/ui/placePacksLoading.js",
"src/ui/ownerFlag.js",
"src/ui/ownerFlagSheet.js",
"src/ui/cockpitContext.js",
"src/ui/cockpitController.js",
"src/ui/cockpitDisplayPortal.js",
@@ -2900,6 +3054,7 @@
"src/layers/vessels/records.js",
"src/layers/vessels/recordPolicy.js",
"src/layers/vessels/snapshotRenderer.js",
"src/layers/vessels/cockpitSubject.js",
"src/data/inputOwnership.js",
"src/annotations/drawMarks.js",
"src/annotations/drawMode.js",
@@ -2956,6 +3111,7 @@
"src/app/layers/transit.js",
"src/data/transit.js",
"src/data/transitFeeds.js",
"src/data/rioRouteNames.js",
"src/data/transitIcons.js",
"src/data/transitPresetStyle.js",
"src/layers/transit/controls.js",
@@ -3239,6 +3395,9 @@
"src/ui/stores/globeActions.js",
"src/ui/stores/recording.js",
"src/ui/i18n.js",
"src/time/viewerClock.js",
"src/time/worldClock.js",
"src/time/julian.js",
"src/annotations/outlineRungs.js",
"src/sources/openFreeMapOutlines.js",
"src/sources/nominatimOutlines.js",
@@ -3259,7 +3418,37 @@
"src/voice/osmActions.js",
"src/layers/osmPlaces/index.js",
"src/app/layers/osmPlaces.js",
"src/voice/areaActions.js"
"src/voice/areaActions.js",
"src/app/layers/wildlifeSightings.js",
"src/layers/wildlifeSightings/cards.js",
"src/layers/wildlifeSightings/controls.js",
"src/layers/wildlifeSightings/index.js",
"src/layers/wildlifeSightings/lifecycle.js",
"src/layers/wildlifeSightings/model.js",
"src/layers/wildlifeSightings/photos.js",
"src/layers/wildlifeSightings/policy.js",
"src/layers/wildlifeSightings/records.js",
"src/layers/wildlifeSightings/rendering.js",
"src/layers/wildlifeSightings/density.js",
"src/layers/wildlifeSightings/selection.js",
"src/layers/wildlifeSightings/source.js",
"src/layers/wildlifeSightings/viewport.js",
"src/layers/wildlifeSightings/bounds.js",
"src/layers/wildlifeSightings/acartiaTaxa.js",
"src/ui/wildlifeSightingCard.js",
"src/ui/views/panels/WildlifeSightingCard.vue",
"src/layers/wildlifeSightings/gridViewport.js",
"src/layers/wildlifeSightings/billboardModel.js",
"src/layers/wildlifeSightings/sprites.js",
"src/ui/wildlifeSightingPlacement.js",
"src/overlays/worldOverlayChrome.js",
"src/layers/wildlifeSightings/photoText.js",
"src/layers/wildlifeSightings/performance.js",
"src/layers/satellites/classes.js",
"src/layers/satellites/sensors.js",
"src/data/ucsSatellites.js",
"src/data/imagingSensors.js",
"src/data/planespottersPhoto.js"
],
"external": [
"hls.js",
@@ -3284,6 +3473,8 @@
"application-layer-construction": {
"exports": ["./application/layers"],
"modules": [
"src/data/feedState.js",
"src/loadingFeedback.js",
"src/sources/adsbFields.js",
"src/data/headingDegrees.js",
"src/data/distanceFormat.js",
@@ -3318,6 +3509,7 @@
"src/app/layers/alprCameras.js",
"src/app/layers/bikeshare.js",
"src/app/layers/cctv.js",
"src/app/layers/cyclones.js",
"src/app/layers/earthquakes.js",
"src/app/layers/firms.js",
"src/app/layers/flights.js",
@@ -3368,6 +3560,7 @@
"src/data/labelArbiter.js",
"src/data/idSwitches.js",
"src/data/layerState.js",
"src/data/satelliteClassMask.js",
"src/data/layerStateTokenReservations.json",
"src/data/localGeojsonCore.js",
"src/data/localGeojsonLod.js",
@@ -3396,6 +3589,7 @@
"src/data/scenePick.js",
"src/data/spriteOrder.js",
"src/data/tomtomTiles.js",
"src/data/tileBudget.js",
"src/data/tr3bRegistry.js",
"src/data/trackedCamera.js",
"src/data/trackedModelRegime.js",
@@ -3569,6 +3763,7 @@
"src/layers/radio/tuningNoise.js",
"src/layers/radio/volume.js",
"src/layers/satellites/catalog.js",
"src/layers/satellites/cockpitSubject.js",
"src/layers/satellites/controls.js",
"src/layers/satellites/index.js",
"src/layers/satellites/ingestion.js",
@@ -3656,10 +3851,14 @@
"src/layers/vessels/records.js",
"src/layers/vessels/recordPolicy.js",
"src/layers/vessels/snapshotRenderer.js",
"src/layers/vessels/cockpitSubject.js",
"src/data/inputOwnership.js",
"src/annotations/worldAnnotationRenderer.js",
"src/cameraGroundGuard.js",
"src/cameraVerbs.js",
"src/globeInView.js",
"src/scopeMaskPolicy.js",
"src/scopeMask.js",
"src/services/groundFloor.js",
"src/data/routeSteps.js",
"src/layers/directions/index.js",
@@ -3706,6 +3905,7 @@
"src/sources/nominatim.js",
"src/app/layers/transit.js",
"src/data/transitFeeds.js",
"src/data/rioRouteNames.js",
"src/data/transitIcons.js",
"src/data/transitPresetStyle.js",
"src/layers/transit/controls.js",
@@ -3869,6 +4069,8 @@
"src/ui/stores/bhoteKoshiPanel.js",
"src/hudReadouts.js",
"src/ui/i18n.js",
"src/time/worldClock.js",
"src/time/julian.js",
"src/annotations/outlineRungs.js",
"src/sources/openFreeMapOutlines.js",
"src/sources/nominatimOutlines.js",
@@ -3879,7 +4081,35 @@
"src/data/local_data/wof_neighborhoods/files.js",
"src/layers/osmPlaces/model.js",
"src/layers/osmPlaces/index.js",
"src/app/layers/osmPlaces.js"
"src/app/layers/osmPlaces.js",
"src/app/layers/wildlifeSightings.js",
"src/layers/wildlifeSightings/cards.js",
"src/layers/wildlifeSightings/controls.js",
"src/layers/wildlifeSightings/index.js",
"src/layers/wildlifeSightings/lifecycle.js",
"src/layers/wildlifeSightings/model.js",
"src/layers/wildlifeSightings/photos.js",
"src/layers/wildlifeSightings/policy.js",
"src/layers/wildlifeSightings/records.js",
"src/layers/wildlifeSightings/rendering.js",
"src/layers/wildlifeSightings/density.js",
"src/layers/wildlifeSightings/selection.js",
"src/layers/wildlifeSightings/source.js",
"src/layers/wildlifeSightings/viewport.js",
"src/ui/rail/utcClock.js",
"src/layers/wildlifeSightings/bounds.js",
"src/layers/wildlifeSightings/acartiaTaxa.js",
"src/layers/wildlifeSightings/gridViewport.js",
"src/layers/wildlifeSightings/billboardModel.js",
"src/layers/wildlifeSightings/sprites.js",
"src/overlays/worldOverlayChrome.js",
"src/layers/wildlifeSightings/photoText.js",
"src/layers/wildlifeSightings/performance.js",
"src/layers/satellites/classes.js",
"src/layers/satellites/sensors.js",
"src/data/ucsSatellites.js",
"src/data/imagingSensors.js",
"src/data/planespottersPhoto.js"
],
"external": [
"hls.js",
@@ -3958,6 +4188,7 @@
"src/sources/phaseTiming.js",
"src/sources/militaryTileGeometry.js",
"src/data/tomtomTiles.js",
"src/data/tileBudget.js",
"src/data/installationNames.js",
"src/annotations/outlineRungs.js",
"src/sources/openFreeMapOutlines.js",
@@ -4013,6 +4244,7 @@
"src/data/idSwitches.js",
"src/data/firmsIntensity.js",
"src/data/layerState.js",
"src/data/satelliteClassMask.js",
"src/data/layerStateCoordinator.js",
"src/data/layerStateTokenReservations.json",
"src/loadingFeedback.js",
@@ -4032,7 +4264,8 @@
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
"src/i18n/locales/zh-Hant.json",
"src/time/worldClock.js"
],
"external": []
},
@@ -4045,6 +4278,14 @@
"src/ui/radioPanelPlacement.js",
"src/bloom.js",
"src/cockpitVisionPolicy.js",
"src/cockpitSubjects.js",
"src/cockpitRadar.js",
"src/cockpitLook.js",
"src/cockpitOrbital.js",
"src/cockpitOrbitalFocus.js",
"src/cockpitOrbitalSatellites.js",
"src/geoDistance.js",
"src/data/motionModel.js",
"src/contactsDetectionPolicy.js",
"src/data/detectionPolicy.js",
"src/styles/anime.js",
@@ -4311,6 +4552,7 @@
"src/layers/alpr/records.js",
"src/layers/alpr/source.js",
"src/data/tomtomTiles.js",
"src/data/tileBudget.js",
"src/layers/alpr/tileRecords.js",
"src/sources/httpBody.js",
"src/i18n/marker.js",
@@ -4349,6 +4591,7 @@
"src/data/militaryInstallationData.js",
"src/data/installationNames.js",
"src/data/tomtomTiles.js",
"src/data/tileBudget.js",
"src/sources/capability.js",
"src/sources/httpBody.js",
"src/i18n/marker.js",
@@ -4376,13 +4619,22 @@
},
"satellites-source": {
"exports": ["./layers/satellites/source"],
"modules": ["src/layers/satellites/source.js"],
"modules": [
"src/layers/satellites/source.js",
"src/layers/satellites/classes.js",
"src/layers/satellites/sensors.js",
"src/data/ucsSatellites.js",
"src/data/imagingSensors.js",
"src/data/bundledJson.js",
"src/data/retryableLoad.js"
],
"external": []
},
"traffic-source-factory": {
"exports": ["./layers/traffic/source"],
"modules": [
"src/data/tomtomTiles.js",
"src/data/tileBudget.js",
"src/layers/traffic/flowDecode.js",
"src/layers/traffic/flowSource.js",
"src/layers/traffic/footprint.js",
@@ -4415,7 +4667,15 @@
"src/layers/airQuality/model.js",
"src/layers/airQuality/source.js",
"src/layers/droneAirspace/source.js",
"src/layers/droneAirspace/records.js"
"src/layers/droneAirspace/records.js",
"src/layers/wildlifeSightings/policy.js",
"src/layers/wildlifeSightings/records.js",
"src/layers/wildlifeSightings/source.js",
"src/layers/wildlifeSightings/viewport.js",
"src/layers/wildlifeSightings/bounds.js",
"src/layers/wildlifeSightings/acartiaTaxa.js",
"src/layers/wildlifeSightings/gridViewport.js",
"src/layers/wildlifeSightings/performance.js"
],
"external": []
},
@@ -4467,6 +4727,7 @@
"server/providers/transitHistory.js",
"src/data/gtfsRealtime.js",
"src/data/transitFeeds.js",
"src/data/rioRouteNames.js",
"src/geoDistance.js",
"src/data/transitProxy.js",
"src/sources/httpBody.js",
@@ -4483,6 +4744,7 @@
"modules": [
"src/data/gtfsRealtime.js",
"src/data/transitFeeds.js",
"src/data/rioRouteNames.js",
"src/geoDistance.js",
"src/data/transitProxy.js",
"src/sources/transitHistory.js"
@@ -4516,6 +4778,7 @@
"src/data/terrainHeights.js",
"src/data/transit.js",
"src/data/transitFeeds.js",
"src/data/rioRouteNames.js",
"src/data/transitIcons.js",
"src/data/transitPresetStyle.js",
"src/data/transitProxy.js",
@@ -4563,7 +4826,9 @@
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
"src/i18n/locales/zh-Hant.json",
"src/time/worldClock.js",
"src/overlays/worldOverlayChrome.js"
],
"external": ["cesium", "egm96-universal", "pbf"]
},
@@ -4574,6 +4839,7 @@
"src/data/iconOrientation.js",
"src/data/inputOwnership.js",
"src/data/transitFeeds.js",
"src/data/rioRouteNames.js",
"src/geoDistance.js",
"src/data/transitIcons.js",
"src/data/transitPresetStyle.js",
@@ -4615,7 +4881,8 @@
"src/i18n/locales/ru.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
"src/i18n/locales/zh-Hant.json",
"src/time/worldClock.js"
],
"external": ["cesium", "pbf"]
},
@@ -4631,6 +4898,7 @@
"exports": ["./sources/transit-service"],
"modules": [
"src/data/transitFeeds.js",
"src/data/rioRouteNames.js",
"src/geoDistance.js",
"src/data/transitProxy.js",
"src/data/gtfsRealtime.js",
@@ -4731,12 +4999,14 @@
"src/view/index.js",
"src/data/firmsIntensity.js",
"src/data/layerState.js",
"src/data/satelliteClassMask.js",
"src/data/layerStateTokenReservations.json",
"src/data/idSwitches.js",
"src/data/detectionPolicy.js",
"src/scopeMaskPolicy.js",
"src/i18n/marker.js",
"src/visualStylePolicy.js"
"src/visualStylePolicy.js",
"src/time/worldClock.js"
],
"external": []
},
@@ -4856,6 +5126,7 @@
"src/data/idSwitches.js",
"src/data/vesselTypes.js",
"src/data/layerState.js",
"src/data/satelliteClassMask.js",
"src/data/layerStateTokenReservations.json",
"src/sources/gbfsStations.js",
"src/sources/tle.js",
@@ -4875,6 +5146,7 @@
"src/tools/surfaces.js",
"src/layers/transit/policy.js",
"src/data/transitFeeds.js",
"src/data/rioRouteNames.js",
"src/layers/transit/movement.js",
"src/data/contactPlayback.js",
"src/data/layerSnapshot.js",
@@ -4937,6 +5209,7 @@
"src/sources/vectorTiles.js",
"src/sources/phaseTiming.js",
"src/data/tomtomTiles.js",
"src/data/tileBudget.js",
"src/sources/capability.js",
"src/data/militaryInstallationData.js",
"src/layers/launches/source.js",
@@ -4978,7 +5251,12 @@
"src/layers/alpr/records.js",
"src/layers/alpr/tileRecords.js",
"src/sources/infrastructureData.js",
"src/sources/eventPacks.js"
"src/sources/eventPacks.js",
"src/layers/satellites/classes.js",
"src/layers/satellites/sensors.js",
"src/data/ucsSatellites.js",
"src/data/imagingSensors.js",
"src/data/planespottersPhoto.js"
],
"external": ["@mapbox/vector-tile", "pbf"]
},
@@ -4989,6 +5267,7 @@
"src/data/idSwitches.js",
"src/data/firmsIntensity.js",
"src/data/layerState.js",
"src/data/satelliteClassMask.js",
"src/data/layerStateTokenReservations.json",
"src/visualStylePolicy.js"
],
@@ -5042,5 +5321,68 @@
"src/i18n/marker.js"
],
"external": ["cesium"]
},
"wildlife-sightings": {
"exports": ["./layers/wildlife-sightings"],
"modules": [
"src/data/dataCredits.js",
"src/data/iconOrientation.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"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/i18n/marker.js",
"src/layers/wildlifeSightings/cards.js",
"src/layers/wildlifeSightings/controls.js",
"src/layers/wildlifeSightings/index.js",
"src/layers/wildlifeSightings/lifecycle.js",
"src/layers/wildlifeSightings/model.js",
"src/layers/wildlifeSightings/photos.js",
"src/layers/wildlifeSightings/policy.js",
"src/layers/wildlifeSightings/records.js",
"src/layers/wildlifeSightings/rendering.js",
"src/layers/wildlifeSightings/density.js",
"src/layers/wildlifeSightings/selection.js",
"src/layers/wildlifeSightings/source.js",
"src/layers/wildlifeSightings/viewport.js",
"src/overlays/worldOverlayDraw.js",
"src/overlays/worldOverlayTokens.js",
"src/sources/httpBody.js",
"src/layers/wildlifeSightings/bounds.js",
"src/layers/wildlifeSightings/acartiaTaxa.js",
"src/ui/statusText.js",
"src/ui/layerNames.js",
"src/layers/wildlifeSightings/gridViewport.js",
"src/layers/wildlifeSightings/billboardModel.js",
"src/layers/wildlifeSightings/sprites.js",
"src/layers/wildlifeSightings/photoText.js",
"src/layers/wildlifeSightings/performance.js"
],
"external": ["cesium"]
},
"wildlife-sightings-source": {
"exports": ["./layers/wildlife-sightings/source"],
"modules": [
"src/i18n/marker.js",
"src/layers/wildlifeSightings/policy.js",
"src/layers/wildlifeSightings/records.js",
"src/layers/wildlifeSightings/source.js",
"src/layers/wildlifeSightings/viewport.js",
"src/sources/httpBody.js",
"src/layers/wildlifeSightings/bounds.js",
"src/layers/wildlifeSightings/acartiaTaxa.js",
"src/layers/wildlifeSightings/gridViewport.js",
"src/layers/wildlifeSightings/performance.js"
],
"external": []
}
}
+3
View File
@@ -4,6 +4,7 @@ import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import puppeteer from 'puppeteer';
import { stubPlanespotters } from './qa-photo-stub.mjs';
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const shotsDir = path.join(repoRoot, 'qa-shots', 'cockpit-utility');
@@ -29,6 +30,7 @@ const browser = await puppeteer.launch({
],
});
const page = await browser.newPage();
await stubPlanespotters(page);
const failures = [];
const consoleErrors = [];
const localHttpErrors = [];
@@ -825,6 +827,7 @@ try {
// a legitimate retained snapshot cannot satisfy or invalidate a FIRST-fetch
// assertion when camera motion supersedes the manager's initial update.
const readinessPage = await browser.newPage();
await stubPlanespotters(readinessPage);
let deferredInstallationReadiness;
try {
await readinessPage.setViewport({ width: 1440, height: 900 });
+2
View File
@@ -28,6 +28,7 @@
import fs from 'node:fs';
import path from 'node:path';
import puppeteer from 'puppeteer';
import { stubPlanespotters } from './qa-photo-stub.mjs';
import { createScreencast, moveCamera } from './qa-journey-recorder.mjs';
const argv = process.argv.slice(2);
@@ -77,6 +78,7 @@ const browser = await puppeteer.launch({
async function openPage({ aircraft = false } = {}) {
const page = await browser.newPage();
await stubPlanespotters(page);
await page.setViewport({ width: 1440, height: 900 });
page.on('console', (message) => {
if (message.type() === 'error') result.consoleErrors.push(message.text());
+2
View File
@@ -27,6 +27,7 @@
*/
import puppeteer from 'puppeteer';
import { stubPlanespotters } from './qa-photo-stub.mjs';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
@@ -223,6 +224,7 @@ async function main() {
const errors = [];
try {
const page = await browser.newPage();
await stubPlanespotters(page);
await page.setViewport({ width: 1440, height: 900 });
page.on('pageerror', (e) => errors.push(e.message));
const { lon, lat } = CENTER;
+2
View File
@@ -21,6 +21,7 @@ import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import puppeteer from 'puppeteer';
import { stubPlanespotters } from './qa-photo-stub.mjs';
const argv = process.argv.slice(2);
const getOpt = (name, fallback = null) => {
@@ -651,6 +652,7 @@ async function main() {
],
});
const page = await browser.newPage();
await stubPlanespotters(page);
await page.setViewport(VIEWPORT);
const consoleMessages = [];
page.on('console', (message) => {
+73
View File
@@ -28,6 +28,8 @@
* (vii) FAR SIDE — pins skip the depth test, so they are horizon-culled:
* seen from over the Indian Ocean no pin clearly behind the planet is
* shown; back over North America the near-side pins show again.
* (viii) AGE — seven days is the default; shorter/longer windows and All
* markers update the globe, and the age choice survives a reload.
*
* Screenshots go to qa-shots/ghostmaps-*.png (gitignored).
*
@@ -224,6 +226,77 @@ async function main() {
record('LIVE: the /api/ghostmaps/events stream is open', stream);
await page.screenshot({ path: path.join(SHOTS_DIR, 'ghostmaps-live.png') });
const ageChips = await page.evaluate(
(id) =>
[
...document.querySelectorAll(
`[data-layer-id="${id}"] [data-segmented-id="age"] [role="radio"]`,
),
].map((option) => ({
id: option.dataset.chipId,
active: option.getAttribute('aria-checked') === 'true',
})),
LAYER_ID,
);
record(
'AGE: seven days is the default with an All markers option',
live.ageDays === 7 &&
ageChips.length === 6 &&
ageChips
.filter((chip) => chip.active)
.map((chip) => chip.id)
.join() === 'age-7' &&
ageChips.some((chip) => chip.id === 'age-all'),
);
const selectAge = async (days) => {
await showRow(page);
const selector = `[data-layer-id="${LAYER_ID}"] [data-chip-id="age-${days ?? 'all'}"]`;
await page.waitForSelector(selector, { timeout: 15000 });
// Dispatch through the rendered button, matching the product-chip gate.
await page.$eval(selector, (button) => button.click());
await page.waitForFunction(
(id, age) => {
const s = window.__godsEyeView.dataManager.layers
.get(id)
.module.getStats();
return s.ageDays === age && !s.loading;
},
{ timeout: TIMEOUT_MS },
LAYER_ID,
days,
);
return stats(page);
};
const all = await selectAge(null);
record(
'AGE: All markers restores old events without duplicate data sources',
all.count > live.count &&
(await sources(page)).length === live.products.length,
`${live.count} → ${all.count}`,
);
await page.reload({ waitUntil: 'domcontentloaded' });
await page.waitForFunction(() => window.__godsEyeView?.dataManager, {
timeout: TIMEOUT_MS,
});
await enableAndSettle(page);
const persistedAge = await stats(page);
record(
'AGE: All markers survives a reload',
persistedAge.ageDays === null && persistedAge.count === all.count,
);
const thirty = await selectAge(30);
const one = await selectAge(1);
const seven = await selectAge(7);
record(
'AGE: counts follow the window order and seven days restores the default',
one.count <= live.count &&
thirty.count >= live.count &&
thirty.count <= all.count &&
seven.count === live.count &&
seven.ageDays === 7,
`1d=${one.count}, 7d=${seven.count}, 30d=${thirty.count}, all=${all.count}`,
);
// ---- (ii) PICK ----------------------------------------------------------
const target = await page.evaluate((id) => {
const gev = window.__godsEyeView;
+2
View File
@@ -99,6 +99,7 @@
import fs from 'node:fs';
import path from 'node:path';
import puppeteer from 'puppeteer';
import { stubPlanespotters } from './qa-photo-stub.mjs';
// ---------------------------------------------------------------------------
// Args (same shape as track-regression.mjs)
@@ -466,6 +467,7 @@ async function main() {
const failedResponses = [];
try {
const page = await browser.newPage();
await stubPlanespotters(page);
await page.setViewport({ width: 1280, height: 800 });
page.on('console', (msg) => {
if (msg.type() === 'error') {
+12 -12
View File
@@ -239,11 +239,11 @@ function satisfiesEngines(version, range) {
* re-resolve the interpreter from PATH and hand the gate a different Node than
* the one the check believes it selected.
* @param {string} version e.g. "v24.19.0"
* @returns {boolean} true only for a parseable Node 24.
* @returns {boolean} true for a parseable supported Node 24 or 26.
*/
function isCalibratedAllocationRuntime(version) {
const [maj] = String(version || '').trim().replace(/^v/, '').split('.').map((n) => Number.parseInt(n, 10));
return maj === 24;
return maj === 24 || maj === 26;
}
/**
@@ -546,7 +546,7 @@ const check = (spec) => { CHECKS.push(spec); };
// ─── A · REPO GATES ───────────────────────────────────────────────────────
check({
id: 'A1', group: 'A', desc: 'Node runtime satisfies package.json engines (allocation budgets are pinned to Node 24)',
id: 'A1', group: 'A', desc: 'Node runtime satisfies package.json engines (allocation budgets cover supported Node majors)',
run: async () => {
const pkg = JSON.parse(readFileSync(resolve(REPO_ROOT, 'package.json'), 'utf8'));
const range = pkg.engines?.node || '(unset)';
@@ -557,7 +557,7 @@ check({
if (satisfied) return pass(`node ${process.versions.node} satisfies "${range}"`);
// An off-range runtime is an environment problem, not a product defect —
// but it silently disables the allocation gate, so say so loudly. A3 then
// runs that gate under a discovered Node 24.
// runs that gate under a discovered calibrated Node 24 fallback.
return env.node24
? skip(`node ${process.versions.node} is OUTSIDE "${range}"; A3 runs the allocation gate under ${env.node24.label}, but prefer running the whole L9 pass on Node 24`, 'ENV')
: fail(`node ${process.versions.node} is OUTSIDE "${range}" and no Node 24 runtime was found — the allocation gate cannot run at all`);
@@ -579,13 +579,13 @@ check({
});
check({
id: 'A3', group: 'A', desc: 'Allocation microbenchmarks actually EXECUTE (they silently skip off Node 24)',
id: 'A3', group: 'A', desc: 'Allocation microbenchmarks actually EXECUTE on supported Node majors',
run: async () => {
// npm test prints "[unit] SKIPPED n allocation microbenchmarks" and still
// exits 0 when the runtime is not Node 24. A green suite is therefore NOT
// proof the gate ran. Force it, under Node 24 when one is discoverable.
// exits 0 when the runtime is outside the calibrated supported majors. A green
// suite is therefore NOT proof the gate ran. Force it under a calibrated runtime.
//
// ALWAYS invoke the Node 24 BINARY directly — never `npm test`. npm
// ALWAYS invoke the calibrated Node binary directly — never `npm test`. npm
// re-resolves the interpreter from PATH and can land back on the system
// Node even when THIS process is already 24: running the whole matrix under
// `mise exec [email protected] --`, the npm shell-out still re-execed system
@@ -597,10 +597,10 @@ check({
const v = await sh(bin, ['--version'], { timeoutMs: 60000 });
const version = (v.out || '').trim();
if (!isCalibratedAllocationRuntime(version)) {
// run-unit-tests.mjs refuses to measure calibrated budgets off Node 24,
// run-unit-tests.mjs refuses to measure calibrated budgets off supported Node majors,
// so a wrong binary means the gate never ran. That is this check failing
// to establish its own claim — not evidence about the product.
return crash(`the runtime selected for the allocation gate (${label}) reports ${version || 'no parseable version'}, not Node 24 — the gate would refuse or silently skip, so this check verified nothing`);
return crash(`the runtime selected for the allocation gate (${label}) reports ${version || 'no parseable version'}, not Node 24 or 26 — the gate would refuse or silently skip, so this check verified nothing`);
}
const r = await sh(bin, [resolve(REPO_ROOT, 'scripts/run-unit-tests.mjs')], {
timeoutMs: 600000, env: { GEV_REQUIRE_ALLOCATION_GATE: '1' },
@@ -611,9 +611,9 @@ check({
};
const [maj] = process.versions.node.split('.').map(Number);
if (maj === 24) return runGate(process.execPath, 'this runtime');
if (maj === 24 || maj === 26) return runGate(process.execPath, 'this runtime');
if (env.node24) return runGate(env.node24.bin, env.node24.label);
return skip(`no Node 24 runtime found (running ${process.versions.node}); the gate SKIPS silently — install Node 24 and re-run`, 'OWNER-RUN');
return skip(`no calibrated Node 24/26 runtime found (running ${process.versions.node}); the gate SKIPS silently — install a supported Node runtime and re-run`, 'OWNER-RUN');
},
});
+78
View File
@@ -0,0 +1,78 @@
/**
* Keep browser QA off third-party aircraft photo services.
*
* Tracking a civil flight asks Planespotters for the aircraft's photo. QA
* runs track synthetic hexes, so a real request only adds a third-party
* dependency and, when the service refuses a headless browser, a console
* error that fails the "no console errors" checks. Every photo lookup is
* answered with no photos instead, without leaving the browser.
*/
/** Origin of the aircraft photo lookup. */
export const PLANESPOTTERS_ORIGIN = 'https://api.planespotters.net';
/** The answer every stubbed lookup gets: no photos for this aircraft. */
export const PLANESPOTTERS_EMPTY_BODY = JSON.stringify({ photos: [] });
/** Whether a URL is a photo lookup the stub answers. */
export function isPlanespottersRequest(url) {
try {
return new URL(String(url)).origin === PLANESPOTTERS_ORIGIN;
} catch {
return false;
}
}
/**
* Answer an intercepted photo lookup from a page that already uses request
* interception. Returns false for any other request, which the caller
* handles as before.
* @param {import('puppeteer').HTTPRequest} request
* @returns {boolean} Whether the request was answered.
*/
export function respondToPlanespotters(request) {
if (!isPlanespottersRequest(request.url())) return false;
request.respond({
status: 200,
contentType: 'application/json',
headers: { 'Access-Control-Allow-Origin': '*' },
body: PLANESPOTTERS_EMPTY_BODY,
});
return true;
}
/**
* Stub photo lookups in a page that does not intercept requests: wraps
* `fetch` before any app code runs, so a lookup never reaches the network.
* Composes with other `fetch` shims in either order.
* @param {import('puppeteer').Page} page
* @returns {Promise<void>}
*/
export async function stubPlanespotters(page) {
await page.evaluateOnNewDocument(
(origin, body) => {
const original = window.fetch.bind(window);
window.fetch = (input, init) => {
const href =
typeof input === 'string' ? input : input?.url || String(input);
let target = '';
try {
target = new URL(href, location.href).origin;
} catch {
/* not a URL */
}
if (target === origin) {
return Promise.resolve(
new Response(body, {
status: 200,
headers: { 'Content-Type': 'application/json' },
}),
);
}
return original(input, init);
};
},
PLANESPOTTERS_ORIGIN,
PLANESPOTTERS_EMPTY_BODY,
);
}
+2
View File
@@ -32,6 +32,7 @@
import fs from 'node:fs';
import path from 'node:path';
import puppeteer from 'puppeteer';
import { stubPlanespotters } from './qa-photo-stub.mjs';
import { classifyAircraft, CLASS_SCALE_2D, CLASS_SCALE_3D, CLASS_MODEL_REAL } from '../src/data/aircraftClass.js';
// Hangar fleet (2026-08-16): real per-class GLBs render at scale 1; military
// heavies (airliner/quadjet/glider) render airplane.glb at 1 x class; only
@@ -186,6 +187,7 @@ async function main() {
const httpErrors = [];
try {
const page = await browser.newPage();
await stubPlanespotters(page);
await page.setViewport({ width: 1280, height: 800 });
page.on('console', (msg) => {
if (msg.type() === 'error') {
+33 -1
View File
@@ -296,6 +296,22 @@ async function expandPanel(page) {
);
}
/** A keyless bundle opens on the first keyless source with live controls. */
async function assertKeylessDefault(page) {
await page.evaluate(() =>
window.__godsEyeView.dataManager.setEnabled('street-level', true, {
origin: 'user',
}),
);
await uiUntil(page, litAlone, 'kartaview');
const shown = await page.evaluate(() => ({
status: document.getElementById('sl-status').textContent,
disabled: document.getElementById('sl-controls').disabled,
}));
assert.notEqual(shown.status, 'KEY REQUIRED');
assert.equal(shown.disabled, false, 'controls stay live');
}
async function assertKeyRequired(page) {
await page.evaluate(() =>
window.__godsEyeView.dataManager.setEnabled('street-level', true, {
@@ -528,11 +544,25 @@ async function main() {
);
await expandPanel(page);
if (!configured) {
if (!configured && litAlone(await getUI(page), 'mapillary')) {
// A keyed bundle behind a server without the token keeps Mapillary.
await step('keyless server: KEY REQUIRED gates the controls', () =>
assertKeyRequired(page),
);
skip('no Mapillary key', 'the keyed steps');
} else if (!configured) {
await step('keyless server: opens on KartaView, not KEY REQUIRED', () =>
assertKeylessDefault(page),
);
await step(
'keyless server: the MAPILLARY chip explains its key instead of switching',
async () => {
await press(page, '#sl-provider-chips [data-chip-id="mapillary"]');
await new Promise((resolve) => setTimeout(resolve, 500));
assert.ok(litAlone(await getUI(page), 'kartaview'));
},
);
skip('no Mapillary key', 'the keyed steps');
} else {
await park(page);
await step(
@@ -751,6 +781,8 @@ async function main() {
await park(page);
await fixtureStep(
// The fixture bundle carries a token, so this covers a keyless
// server behind a keyed bundle; the keyless default is checked above.
'keyless server (second page): KEY REQUIRED gates the controls',
async () => {
const keyless = await open(
+19 -10
View File
@@ -2321,9 +2321,11 @@ async function runBehaviorLayer() {
`result=${JSON.stringify(r)?.slice(0, 120)} state=${JSON.stringify(contextExited)?.slice(0, 80)}`,
);
// (9f) With Contacts OFF the entry gate is shut. Entry must refuse with the
// reason rather than produce the half-entered state the operator saw: a
// plane anchored under the camera with no HUD and no exit control.
// (9f) Cockpit needs only the tracked aircraft's own layer, not Contacts.
// With Contacts OFF, status must explain a shut gate without blaming
// Contacts, and entry must either enter cleanly or refuse with the reason
// rather than produce the half-entered state the operator saw: a plane
// anchored under the camera with no HUD and no exit control.
r = await run('control_cockpit', { action: 'status' });
const gateReport = r?.state || {};
const cockpitBeforeGate = await page.evaluate(() =>
@@ -2331,10 +2333,13 @@ async function runBehaviorLayer() {
);
report(
gateReport.active === false &&
gateReport.entryAllowed === false &&
gateReport.entryBlockedReason === 'contacts-inactive' &&
(gateReport.entryAllowed === true
? gateReport.entryBlockedReason === null
: ['subject-layer-off', 'no-tracked-aircraft'].includes(
gateReport.entryBlockedReason,
)) &&
!cockpitBeforeGate,
'behavior: cockpit status names why entry is blocked with Contacts off',
'behavior: cockpit status explains its gate without requiring Contacts',
`state=${JSON.stringify(gateReport)?.slice(0, 150)}`,
);
@@ -2348,12 +2353,16 @@ async function runBehaviorLayer() {
Boolean(window.__godsEyeView?.styleManager?.cockpitView?.active),
);
report(
gated?.ok === false &&
/contacts/i.test(gated?.error || '') &&
!cockpitAfterGate,
'behavior: cockpit entry with Contacts off is refused, not half-entered',
gated?.ok === true
? cockpitAfterGate
: !cockpitAfterGate && !/contacts/i.test(gated?.error || ''),
'behavior: cockpit entry with Contacts off follows the own-layer gate, never half-entered',
`ok=${gated?.ok} error=${String(gated?.error || '').slice(0, 90)} active=${cockpitAfterGate}`,
);
if (cockpitAfterGate)
await page.evaluate(() =>
window.__godsEyeView?.styleManager?.controlCockpit?.('exit'),
);
await runBundledOutlineScenarios({
page,
+59
View File
@@ -0,0 +1,59 @@
import { spawnSync } from 'node:child_process';
import path from 'node:path';
import { pathToFileURL } from 'node:url';
import {
discoverUnitTestFiles,
UNIT_TEST_NODE_FLAGS,
} from './run-unit-tests.mjs';
/** Discover every layer/provider benchmark, including those added by other branches. */
export function discoverPerfTestFiles(root = process.cwd()) {
return discoverUnitTestFiles(root).filter((file) =>
file.endsWith('.perf.test.mjs'),
);
}
/** Shared by benchmarks so normal unit runs visibly skip timing assertions. */
export function perfBudgetTestOptions(env = process.env) {
return {
skip:
env.GEV_PERF_BUDGETS === '1'
? false
: 'Timing budgets are opt-in; run npm run test:perf (GEV_PERF_BUDGETS=1). Deterministic delta/write checks still run in the normal suite.',
};
}
/** Build a serial invocation so benchmarks do not compete for CPU time. */
export function perfTestArgs(files) {
// A single serial test process keeps benchmark files from competing with
// each other; the normal suite still discovers them and reports their skips.
return [
...UNIT_TEST_NODE_FLAGS,
'--test',
'--test-concurrency=1',
'--test-isolation=none',
...files,
];
}
/** Run discovered benchmarks with wall-clock budgets enabled. */
export function runPerfTests({
root = process.cwd(),
env = process.env,
run = spawnSync,
} = {}) {
const files = discoverPerfTestFiles(root);
if (!files.length) throw new Error('No *.perf.test.mjs benchmarks found');
const result = run(process.execPath, perfTestArgs(files), {
cwd: root,
stdio: 'inherit',
env: { ...env, GEV_PERF_BUDGETS: '1' },
});
if (result.error) throw result.error;
return result.status ?? 1;
}
const invoked = process.argv[1]
? pathToFileURL(path.resolve(process.argv[1])).href
: '';
if (import.meta.url === invoked) process.exitCode = runPerfTests();
+11 -11
View File
@@ -8,15 +8,16 @@ export const ALLOCATION_TEST_FILES = Object.freeze([
'src/overlays/worldOverlayAllocation.test.mjs',
]);
/** Whether this runtime matches the one the allocation budgets were calibrated on. */
/** Whether this runtime matches a supported major the allocation budgets are calibrated on. */
export function isCalibratedAllocationRuntime(version = process.versions.node) {
return Number.parseInt(String(version).split('.')[0], 10) === 24;
const major = Number.parseInt(String(version).split('.')[0], 10);
return major === 24 || major === 26;
}
/** Require the runtime on which allocation budgets were calibrated. */
export function assertNode24AllocationRuntime(version = process.versions.node) {
/** Require a runtime on which allocation budgets are calibrated. */
export function assertCalibratedAllocationRuntime(version = process.versions.node) {
if (!isCalibratedAllocationRuntime(version)) {
throw new Error(`Allocation budgets require the calibrated Node 24 runtime; received ${version}`);
throw new Error(`Allocation budgets require calibrated Node 24 or 26; received ${version}`);
}
return version;
}
@@ -92,19 +93,18 @@ export function runUnitTests() {
const parallelStatus = runTests(['--test', ...plan.parallel]);
if (parallelStatus !== 0) return parallelStatus;
// The GC-bracketed budgets are calibrated on Node 24 and are meaningless on
// other allocators. A contributor's suite must stay green on any supported
// engine (package.json permits >=24), so uncalibrated runtimes skip the
// The GC-bracketed budgets are calibrated on every supported Node major.
// Unsupported runtimes may still run ordinary tests, so uncalibrated runtimes skip the
// probes with a warning. Set GEV_REQUIRE_ALLOCATION_GATE=1 (pinned CI /
// release batteries) to make an uncalibrated runtime a hard failure.
if (!isCalibratedAllocationRuntime()) {
if (process.env.GEV_REQUIRE_ALLOCATION_GATE === '1') {
assertNode24AllocationRuntime();
assertCalibratedAllocationRuntime();
}
console.warn(
`[unit] SKIPPED ${ALLOCATION_TEST_FILES.length} allocation microbenchmarks: `
+ `budgets are calibrated for Node 24, running ${process.versions.node}. `
+ 'Run under Node 24 (or set GEV_REQUIRE_ALLOCATION_GATE=1 to fail instead).',
+ `budgets are calibrated for Node 24 and 26, running ${process.versions.node}. `
+ 'Run under Node 24 or 26 (or set GEV_REQUIRE_ALLOCATION_GATE=1 to fail instead).',
);
return 0;
}
+12
View File
@@ -94,6 +94,7 @@
import fs from 'node:fs';
import puppeteer from 'puppeteer';
import { respondToPlanespotters } from './qa-photo-stub.mjs';
import { arrivalAngleViolated, arrivalRotationPassed } from '../src/tooling/trackArrivalObservation.mjs';
import { classifyAircraft, CLASS_SCALE_3D, CLASS_MODEL_REAL } from '../src/data/aircraftClass.js';
import { ensureGeoidReady, geoidHeight } from '../src/data/geoid.js';
@@ -287,6 +288,8 @@ async function main() {
});
return;
}
// Aircraft photo lookups never reach the third party: no photos.
if (respondToPlanespotters(request)) return;
if (url.origin === APP_ORIGIN && url.pathname === '/api/openai/hud-summary') {
request.respond({
status: 200,
@@ -409,6 +412,15 @@ async function main() {
status: 'ready',
}));
}
// CelesTrak SATCAT, which the satellite classes read beside the TLEs.
// An empty CSV (header only) keeps the run hermetic: the classes fall
// back to names and groups, as they do when SATCAT is unavailable.
if (isAppRequest && url.pathname.startsWith('/api/celestrak-satcat/')) {
return Promise.resolve(new Response(
'OBJECT_NAME,OBJECT_ID,NORAD_CAT_ID,OBJECT_TYPE,OPS_STATUS_CODE,LAUNCH_DATE\n',
{ status: 200, headers: { 'Content-Type': 'text/csv' } },
));
}
// CelesTrak TLE groups. Served from a fixed two-satellite catalog so
// the satellite assertions are deterministic and the run never depends
// on an upstream that rate-limits (it was answering 403 the night this