diff --git a/server/providers/cctv/constants.js b/server/providers/cctv/constants.js index 257c292b3..a26fa6c20 100644 --- a/server/providers/cctv/constants.js +++ b/server/providers/cctv/constants.js @@ -18,7 +18,7 @@ export const CCTV_MAX_SOURCES_CEILING = 5000; export const AUSTIN_DOWNTOWN = { lat: 30.2672, lon: -97.7431 }; /** Caltrans CCTV: one JSON feed per district, identical schema statewide. */ /** TxDOT ITS: one keyless JSON catalog per district (25 districts statewide). */ -export const TXDOT_ORIGIN = 'https://its.txdot.gov'; +const TXDOT_ORIGIN = 'https://its.txdot.gov'; export const TXDOT_CCTV_STATUS_URL = (district) => `${TXDOT_ORIGIN}/its/DistrictIts/GetCctvStatusListByDistrict?districtCode=${encodeURIComponent(district)}`; /** Per-camera frame. Returns JSON `{snippet:}`, not an image body; diff --git a/server/providers/cctv/groundHeights.js b/server/providers/cctv/groundHeights.js index c73b6e840..f20b9758d 100644 --- a/server/providers/cctv/groundHeights.js +++ b/server/providers/cctv/groundHeights.js @@ -3,7 +3,7 @@ import path from 'node:path'; import { poseHash } from '../../../src/data/cctvFootprint.js'; /** Sidecar written by scripts/precompute-cctv-heights.mjs. */ -export const DEFAULT_GROUND_HEIGHTS_FILE = +const DEFAULT_GROUND_HEIGHTS_FILE = 'src/data/local_data/cctv_ground_heights/cctv_ground_heights.json'; /** @type {{path:string, mtimeMs:number, cameras:object}|null} */ diff --git a/server/providers/cctv/media.js b/server/providers/cctv/media.js index 1323bc841..206a24888 100644 --- a/server/providers/cctv/media.js +++ b/server/providers/cctv/media.js @@ -92,7 +92,7 @@ export function buildSyntheticCctvSvg({ cameraId, label, city, status }) { * @param {ReadableStream|NodeJS.ReadableStream|null} body * @returns {import('stream').Readable|null} */ -export function toReadable(body) { +function toReadable(body) { if (!body) return null; if (typeof body.pipe === 'function') return body; if (typeof body.getReader === 'function') { @@ -108,7 +108,7 @@ export function toReadable(body) { * @returns {{essence:string,params:Map}|null} Null when the * value is missing or is not a `type/subtype` media type. */ -export function parseMediaType(value) { +function parseMediaType(value) { const [head, ...rest] = String(value ?? '').split(';'); const essence = head.trim().toLowerCase(); if (!/^[a-z0-9!#$&^_.+-]+\/[a-z0-9!#$&^_.+-]+$/.test(essence)) return null; diff --git a/server/providers/cctv/normalize.js b/server/providers/cctv/normalize.js index 74f693dd8..f190f257e 100644 --- a/server/providers/cctv/normalize.js +++ b/server/providers/cctv/normalize.js @@ -64,7 +64,7 @@ export function toFiniteNumber(value, fallback = NaN) { * @param {string} text * @returns {string} */ -export function normalizeKey(text) { +function normalizeKey(text) { return String(text || '') .trim() .toLowerCase() @@ -80,7 +80,7 @@ export function normalizeKey(text) { * @param {string} value * @returns {{lat:number, lon:number}} */ -export function parsePointString(value) { +function parsePointString(value) { const match = String(value || '').match( /POINT\s*\(\s*(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?)\s*\)/i, ); @@ -100,7 +100,7 @@ export function parsePointString(value) { * @param {string|object|null} value * @returns {{lat:number, lon:number}} */ -export function coerceLatLon(value) { +function coerceLatLon(value) { if (!value) return { lat: NaN, lon: NaN }; if (typeof value === 'string') { diff --git a/server/providers/cctv/sources.js b/server/providers/cctv/sources.js index c7def2284..a479a945c 100644 --- a/server/providers/cctv/sources.js +++ b/server/providers/cctv/sources.js @@ -1868,7 +1868,7 @@ export function normalizeCataloniaImageUrl(rawUrl) { * @param {string} imageUrl - A URL normalizeCataloniaImageUrl accepted. * @returns {string} */ -export function cataloniaCameraId(imageUrl) { +function cataloniaCameraId(imageUrl) { const parsed = new URL(imageUrl); const slug = (text) => text diff --git a/server/providers/cctv/stream.js b/server/providers/cctv/stream.js index 14d1743bb..da811f5f9 100644 --- a/server/providers/cctv/stream.js +++ b/server/providers/cctv/stream.js @@ -70,7 +70,7 @@ export async function fetchHlsBytes( } } -export function sameOriginHlsUrl(value, base) { +function sameOriginHlsUrl(value, base) { const url = new URL(value, base); if ( !['https:', 'http:'].includes(url.protocol) || diff --git a/server/providers/cctv/taiwan.js b/server/providers/cctv/taiwan.js index 830ed47fd..a14aabf9a 100644 --- a/server/providers/cctv/taiwan.js +++ b/server/providers/cctv/taiwan.js @@ -12,14 +12,14 @@ export const TAIWAN_FREEWAY_CCTV_URL = 'https://tisvcloud.freeway.gov.tw/history/motc20/CCTV.xml'; /** The whole network is ~1.2 MB. */ export const TAIWAN_MAX_CATALOG_BYTES = 6 * 1024 * 1024; -export const DEFAULT_TAIWAN_MAX_SOURCES = 600; +const DEFAULT_TAIWAN_MAX_SOURCES = 600; /** After a failed download, wait this long before asking again. The catalog * host does not answer from some networks outside Taiwan, and every attempt * there holds the shared catalog refresh until the connect timeout. */ export const TAIWAN_RETRY_AFTER_FAILURE_MS = 60 * 60 * 1000; /** The table is republished daily; a good copy stands in for a failed * download for up to this long. */ -export const TAIWAN_LAST_GOOD_MAX_AGE_MS = 24 * 60 * 60 * 1000; +const TAIWAN_LAST_GOOD_MAX_AGE_MS = 24 * 60 * 60 * 1000; // The table publishes each camera's MJPEG stream. The same camera servers // answer a single JPEG for the same camera ID on a sibling path, which is what @@ -50,7 +50,7 @@ const ANCHORS = [ /** Taiwan main island with slack for the coast; the freeway network has no * offshore-island cameras. */ -export function isLikelyTaiwanCoordinate(lat, lon) { +function isLikelyTaiwanCoordinate(lat, lon) { return ( Number.isFinite(lat) && Number.isFinite(lon) && diff --git a/server/providers/local-receivers.js b/server/providers/local-receivers.js index 994da7bd8..f3f340b54 100644 --- a/server/providers/local-receivers.js +++ b/server/providers/local-receivers.js @@ -27,13 +27,13 @@ import { normalizeDump1090Aircraft } from '../../src/sources/adsbRecords.js'; */ export const LOCAL_RECEIVERS_ROUTE = '/api/local-receivers/aircraft'; -export const LOCAL_RECEIVER_FEEDS_ENV = 'LOCAL_RECEIVER_FEEDS'; -export const LOCAL_RECEIVER_BANDS = Object.freeze(['1090', '978']); -export const LOCAL_RECEIVER_TIMEOUT_MS = 2_000; -export const LOCAL_RECEIVER_MAX_BODY_BYTES = 2 * 1024 * 1024; -export const LOCAL_RECEIVER_CACHE_MS = 1_000; +const LOCAL_RECEIVER_FEEDS_ENV = 'LOCAL_RECEIVER_FEEDS'; +const LOCAL_RECEIVER_BANDS = Object.freeze(['1090', '978']); +const LOCAL_RECEIVER_TIMEOUT_MS = 2_000; +const LOCAL_RECEIVER_MAX_BODY_BYTES = 2 * 1024 * 1024; +const LOCAL_RECEIVER_CACHE_MS = 1_000; /** A feed whose own `now` is older than this is reported stale. */ -export const LOCAL_RECEIVER_STALE_MS = 10_000; +const LOCAL_RECEIVER_STALE_MS = 10_000; const MAX_FEEDS = 8; const LOG_PREFIX = '[local-receivers]'; @@ -139,7 +139,7 @@ class FeedError extends Error { * @param {string} address Resolved IP address. * @returns {boolean} */ -export function isLocalReceiverAddress(address) { +function isLocalReceiverAddress(address) { const value = String(address || '').toLowerCase(); if (value === '::1') return true; const mapped = /^::ffff:(\d{1,3}(?:\.\d{1,3}){3})$/.exec(value); @@ -152,7 +152,7 @@ export function isLocalReceiverAddress(address) { * @param {typeof dnsLookup} lookupImpl `dns.promises.lookup`-compatible. * @returns {Promise>} */ -export async function resolveLocalReceiverAddresses(hostname, lookupImpl) { +async function resolveLocalReceiverAddresses(hostname, lookupImpl) { let resolved; try { resolved = await lookupImpl(hostname, { all: true, verbatim: true }); @@ -274,7 +274,7 @@ function fetchLocalReceiverFeed(url, options) { * @param {object} options * @returns {Promise} Parsed JSON document. */ -export async function fetchLocalReceiverDocument( +async function fetchLocalReceiverDocument( url, { fetchImpl = fetchLocalReceiverFeed, diff --git a/server/providers/mapillary/tiles.js b/server/providers/mapillary/tiles.js index 569e7e45f..6e2207848 100644 --- a/server/providers/mapillary/tiles.js +++ b/server/providers/mapillary/tiles.js @@ -42,7 +42,7 @@ export class TileUpstreamError extends Error { } /** Seconds a Retry-After header asks for (an integer or an HTTP date), else null. */ -export function retryAfterSeconds(header, now = Date.now()) { +function retryAfterSeconds(header, now = Date.now()) { const text = String(header ?? '').trim(); if (!text) return null; const seconds = /^\d+$/.test(text) diff --git a/server/providers/openai/codex-auth.js b/server/providers/openai/codex-auth.js index 5f16ee824..4d3f992da 100644 --- a/server/providers/openai/codex-auth.js +++ b/server/providers/openai/codex-auth.js @@ -145,7 +145,7 @@ const CODEX_LOGIN_ENV = /^(?:PATH|PATHEXT|HOME|USER|USERNAME|LOGNAME|SHELL|LANG|LANGUAGE|LC_[A-Z_]+|TERM|TZ|TMPDIR|TEMP|TMP|XDG_[A-Z_]+|DISPLAY|WAYLAND_DISPLAY|DBUS_SESSION_BUS_ADDRESS|BROWSER|WSL_[A-Z_]+|WSLENV|(?:HTTPS?|ALL|NO)_PROXY|SSL_CERT_(?:FILE|DIR)|NODE_EXTRA_CA_CERTS|SYSTEMROOT|WINDIR|COMSPEC|USERPROFILE|APPDATA|LOCALAPPDATA|PROGRAMDATA|HOMEDRIVE|HOMEPATH|CODEX_[A-Z_]+)$/i; /** The environment `codex login` runs with: the allowlist, then `CODEX_HOME`. */ -export function codexLoginEnvironment(environment, codexHome) { +function codexLoginEnvironment(environment, codexHome) { const env = {}; for (const [name, value] of Object.entries(environment || {})) if (CODEX_LOGIN_ENV.test(name) && value !== undefined) env[name] = value; diff --git a/server/providers/openai/debug-log.js b/server/providers/openai/debug-log.js index 956569642..c8151d2f6 100644 --- a/server/providers/openai/debug-log.js +++ b/server/providers/openai/debug-log.js @@ -157,4 +157,4 @@ function createDebugLogHandler({ }; } -export { createDebugLogHandler, omitVoiceContent }; +export { createDebugLogHandler }; diff --git a/server/providers/radio/catalog.js b/server/providers/radio/catalog.js index 36a3eb730..2864c8986 100644 --- a/server/providers/radio/catalog.js +++ b/server/providers/radio/catalog.js @@ -26,7 +26,7 @@ import { RADIO_UUID_RE, RADIO_FALLBACK_MIRRORS, } from './constants.js'; -export async function mapRadioConcurrent(values, concurrency, mapper) { +async function mapRadioConcurrent(values, concurrency, mapper) { const results = new Array(values.length); let cursor = 0; const workers = Array.from( diff --git a/server/providers/transitHistory.js b/server/providers/transitHistory.js index f396f3d8f..4da5a8891 100644 --- a/server/providers/transitHistory.js +++ b/server/providers/transitHistory.js @@ -1,5 +1,4 @@ export { createTransitHistory, - TRANSIT_HISTORY_LIMITS, validTransitIdentifier, } from '../../src/sources/transitHistoryStore.js'; diff --git a/server/providers/wind/ifs.js b/server/providers/wind/ifs.js index 7767d3905..076baf064 100644 --- a/server/providers/wind/ifs.js +++ b/server/providers/wind/ifs.js @@ -3,7 +3,7 @@ import { decodeWindGribMessage } from './decode.js'; import { resampleWeatherSnapshot, weatherScalarMetadata } from './grid.js'; /** Keyless ECMWF Open Data root for real-time IFS forecasts. */ -export const IFS_BASE = 'https://data.ecmwf.int/forecasts'; +const IFS_BASE = 'https://data.ecmwf.int/forecasts'; /** * Select the latest IFS cycle that should be available. diff --git a/src/annotations/drawMode.js b/src/annotations/drawMode.js index 02a501010..fcfe08be6 100644 --- a/src/annotations/drawMode.js +++ b/src/annotations/drawMode.js @@ -31,10 +31,10 @@ export const MIN_VERTICES = Object.freeze({ area: 3, line: 2, pin: 1 }); */ export const MAX_VERTICES = 512; /** Two clicks closer than this are one vertex: a double-click to finish must not add a stray point. */ -export const MIN_VERTEX_SEPARATION_M = 0.5; +const MIN_VERTEX_SEPARATION_M = 0.5; /** A line shorter than this, or an area thinner than this, is a mis-click rather than a shape. */ -export const MIN_PATH_LENGTH_M = 1; -export const MIN_AREA_M2 = 1; +const MIN_PATH_LENGTH_M = 1; +const MIN_AREA_M2 = 1; /** @param {string} shape @returns {'area'|'line'|'pin'} */ export function normalizeShape(shape) { @@ -50,7 +50,7 @@ export function createDrawSession(shape = 'area') { } /** Great-circle distance in metres between two {lon, lat} points. */ -export function greatCircleM(a, b) { +function greatCircleM(a, b) { return greatCircleMeters(a, b); } diff --git a/src/annotations/drawTool.js b/src/annotations/drawTool.js index 0f4e0fc76..9467406dd 100644 --- a/src/annotations/drawTool.js +++ b/src/annotations/drawTool.js @@ -52,7 +52,7 @@ import { import { msg, registerUnsavedWork, t, translateKnown } from '../i18n/index.js'; /** The id this tool claims the pointer under. */ -export const DRAW_POINTER_OWNER = 'draw'; +const DRAW_POINTER_OWNER = 'draw'; const PREVIEW_DATA_SOURCE_NAME = 'gev-draw-preview'; const COLORS = ['primary', 'amber', 'cyan', 'green', 'red']; diff --git a/src/annotations/resolver.js b/src/annotations/resolver.js index fb5795d53..226caf140 100644 --- a/src/annotations/resolver.js +++ b/src/annotations/resolver.js @@ -29,7 +29,7 @@ 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; +const REGION_FALLBACK_BUDGET_MS = 3_000; /** Entity facts that rule out a state/county reading of the ask. */ const NON_ADMIN_ENTITY_KINDS = new Set([ diff --git a/src/app/embed.js b/src/app/embed.js index a1485fe7e..f0d6cad0a 100644 --- a/src/app/embed.js +++ b/src/app/embed.js @@ -15,9 +15,9 @@ import * as Cesium from 'cesium'; import { announceNavigationAuthority } from '../navigationPolicy.js'; import { annotationsFromParams, createView } from '../view/index.js'; -export const EMBED_VIEW_MESSAGE = 'gev:view'; -export const EMBED_APPLIED_MESSAGE = 'gev:view-applied'; -export const EMBED_READY_MESSAGE = 'gev:ready'; +const EMBED_VIEW_MESSAGE = 'gev:view'; +const EMBED_APPLIED_MESSAGE = 'gev:view-applied'; +const EMBED_READY_MESSAGE = 'gev:ready'; const FOLLOW_LAYERS = { aircraft: 'flights', diff --git a/src/app/sourceComposition.js b/src/app/sourceComposition.js index 0fc619142..04794ab13 100644 --- a/src/app/sourceComposition.js +++ b/src/app/sourceComposition.js @@ -53,7 +53,7 @@ export const CATALOG_SOURCE_CONTRACTS = Object.freeze({ }); /** An omitted provider is unavailable, never an authoritative empty dataset. */ -export class SourceUnavailableError extends Error { +class SourceUnavailableError extends Error { constructor(name) { super(`Data source not configured: ${name}`); this.name = 'SourceUnavailableError'; diff --git a/src/cameraGroundGuard.js b/src/cameraGroundGuard.js index 5a5fa3775..3f9de0cab 100644 --- a/src/cameraGroundGuard.js +++ b/src/cameraGroundGuard.js @@ -20,8 +20,8 @@ import { ownCameraArrival } from './data/cameraArrival.js'; /** Minimum eye height above the rendered surface for a usable view. */ export const MIN_EYE_CLEARANCE_M = 120; /** Retries while tiles stream in; sampling fails until the mesh exists. */ -export const GUARD_ATTEMPTS = 8; -export const GUARD_INTERVAL_MS = 600; +const GUARD_ATTEMPTS = 8; +const GUARD_INTERVAL_MS = 600; /** Ignore sub-metre noise rather than nudging the camera forever. */ const GUARD_EPSILON_M = 2; diff --git a/src/cctvFocusPolicy.js b/src/cctvFocusPolicy.js index 7fb2ebe1b..e11c1e1ca 100644 --- a/src/cctvFocusPolicy.js +++ b/src/cctvFocusPolicy.js @@ -27,7 +27,7 @@ export function runCctvLayerEnableFocus({ * @param {Object} [after={}] Ownership immediately after the await. * @returns {{ trackedEntity: *, cockpitActive: boolean }} Conservative ownership. */ -export function mergeCctvEnableOwnership(before = {}, after = {}) { +function mergeCctvEnableOwnership(before = {}, after = {}) { return { trackedEntity: before.trackedEntity || after.trackedEntity || null, cockpitActive: !!(before.cockpitActive || after.cockpitActive), diff --git a/src/cockpitCloudEffects.js b/src/cockpitCloudEffects.js index ccbf31831..c9117b7d3 100644 --- a/src/cockpitCloudEffects.js +++ b/src/cockpitCloudEffects.js @@ -174,7 +174,7 @@ export function cockpitWeatherEnabledFromStoredValue(value) { * Weather-backed volumetric cloud pass that exists only while cockpit mode is * active. It owns no Cesium fog/post-process stages and cannot affect map mode. */ -export class CockpitCloudEffectsController { +class CockpitCloudEffectsController { constructor(viewer, { weatherService = applicationServices.weather } = {}) { this.weatherService = weatherService; this.viewer = viewer; diff --git a/src/data/adminBoundaries.js b/src/data/adminBoundaries.js index e9f97cf8f..9efa967d4 100644 --- a/src/data/adminBoundaries.js +++ b/src/data/adminBoundaries.js @@ -123,7 +123,7 @@ export function decodeRing(encoded, decimals) { } /** Even-odd point-in-ring test; ring is [[lon, lat], …], open or closed. */ -export function pointInRing(ring, lat, lon) { +function pointInRing(ring, lat, lon) { if (!Array.isArray(ring) || ring.length < 3) return false; let inside = false; for (let i = 0, j = ring.length - 1; i < ring.length; j = i, i += 1) { diff --git a/src/data/aisStreamAdapter.js b/src/data/aisStreamAdapter.js index 1f7b0ef9b..05e747308 100644 --- a/src/data/aisStreamAdapter.js +++ b/src/data/aisStreamAdapter.js @@ -215,7 +215,7 @@ export const AIS_RECOGNIZED_MESSAGE_TYPES = Object.freeze( * @param {Object} body * @returns {string|null} */ -export function aisEnvelopeMmsi(envelope, body = {}) { +function aisEnvelopeMmsi(envelope, body = {}) { const metadata = envelope?.MetaData || envelope?.Metadata || {}; const raw = metadata.MMSI ?? body.UserID ?? body.UserId ?? body.Mmsi; if (raw === null || raw === undefined) return null; diff --git a/src/data/aisWatchdog.js b/src/data/aisWatchdog.js index a83c281d9..f024a6f67 100644 --- a/src/data/aisWatchdog.js +++ b/src/data/aisWatchdog.js @@ -46,28 +46,8 @@ export const AIS_WATCHDOG_DEFAULTS = Object.freeze({ authProbeMs: 3_600_000, }); -/** - * Statuses reported to the browser. 'missing-key' and 'unsupported' keep their - * pre-watchdog meaning so a keyless install still reads as "feature off". - */ -export const AIS_WATCHDOG_STATUSES = Object.freeze([ - 'idle', - 'missing-key', - 'unsupported', - 'connecting', - 'live', - 'stale', - 'reconnecting', - 'down', - 'auth-failed', -]); - /** Failure classes. Only 'transport' is allowed to walk the fast ladder. */ -export const AIS_FAILURE_KINDS = Object.freeze([ - 'transport', - 'auth', - 'rate-limit', -]); +const AIS_FAILURE_KINDS = Object.freeze(['transport', 'auth', 'rate-limit']); /** Statuses that must not flip back to a hopeful 'connecting' on a retry. */ const QUIET_TERMINAL = new Set(['down', 'auth-failed']); diff --git a/src/data/analystEngine.js b/src/data/analystEngine.js index 6a31c5a20..5175fec48 100644 --- a/src/data/analystEngine.js +++ b/src/data/analystEngine.js @@ -62,7 +62,7 @@ const COMMON_FIELDS = Object.freeze(['id', 'lat', 'lon']); /** Derived per-record field for layers with a time field (hours before now). */ const AGE_FIELD = 'ageHours'; /** Spatial scope kinds `query()` accepts. */ -export const ANALYST_SCOPE_KINDS = Object.freeze([ +const ANALYST_SCOPE_KINDS = Object.freeze([ 'view', 'region', 'radius', @@ -74,7 +74,7 @@ export const ANALYST_SCOPE_KINDS = Object.freeze([ * @param {string[]} layerKeys Queried layer ids. * @returns {string[]} Field names, common fields first. */ -export function analystFieldsFor(layerKeys) { +function analystFieldsFor(layerKeys) { const fields = new Set(COMMON_FIELDS); for (const key of layerKeys) { const layer = ANALYST_LAYERS[key]; @@ -104,44 +104,6 @@ export function haversineKm(lat1, lon1, lat2, lon2) { return greatCircleKm({ lat: lat1, lon: lon1 }, { lat: lat2, lon: lon2 }); } -/** - * One filter: {field, op:'gt'|'lt'|'gte'|'lte'|'eq'|'neq'|'contains', value}. - * With `time: true`, both sides compare as epoch ms (ISO strings accepted). - */ -export function applyFilter(records, filter, { time = false } = {}) { - const { field, op } = filter || {}; - if (!field || !op) return records; - const value = time ? toEpochMs(filter.value) : filter.value; - return records.filter((r) => { - const got = time ? toEpochMs(r[field]) : r[field]; - if (got === null || got === undefined || (time && !Number.isFinite(got))) - return false; - if (time && (op === 'eq' || op === 'neq')) - return (got === value) === (op === 'eq'); - switch (op) { - case 'gt': - return Number(got) > Number(value); - case 'gte': - return Number(got) >= Number(value); - case 'lt': - return Number(got) < Number(value); - case 'lte': - return Number(got) <= Number(value); - case 'eq': { - if (typeof got === 'boolean' || typeof value === 'boolean') - return Boolean(got) === Boolean(value); - return String(got).toLowerCase() === String(value).toLowerCase(); - } - case 'neq': - return String(got).toLowerCase() !== String(value).toLowerCase(); - case 'contains': - return String(got).toLowerCase().includes(String(value).toLowerCase()); - default: - return false; - } - }); -} - /** Scope records spatially. scope: {kind:'view'|'region'|'radius'|'anywhere', …}. */ export function applyScope(records, scope, resolved) { if (!scope || scope.kind === 'anywhere') return records; @@ -191,7 +153,7 @@ const COMMON_UNITS = Object.freeze({ * @param {string} field Field name. * @returns {'number'|'text'|'flag'|'time'|null} Field type. */ -export function analystFieldType(layerKeys, field) { +function analystFieldType(layerKeys, field) { if (!analystFieldsFor(layerKeys).includes(field)) return null; if (COMMON_TYPES[field]) return COMMON_TYPES[field]; for (const key of layerKeys) { diff --git a/src/data/bhoteKoshiLocator.js b/src/data/bhoteKoshiLocator.js index 79aab080f..369465680 100644 --- a/src/data/bhoteKoshiLocator.js +++ b/src/data/bhoteKoshiLocator.js @@ -14,7 +14,7 @@ import { BHOTE_KOSHI_FLOOD_PATH } from './bhoteKoshiFloodPath.js'; import { INCIDENT_OVERVIEW_PLACES } from './bhoteKoshiIncidentPlaces.js'; import { t, msg, getLocale } from '../i18n/index.js'; -export const BHOTE_KOSHI_LOCATOR_LAYER_ID = 'bhote-koshi-locator'; +const BHOTE_KOSHI_LOCATOR_LAYER_ID = 'bhote-koshi-locator'; export const BHOTE_KOSHI_LOCATOR_OVERLAY_SOURCE_ID = 'bhote-koshi-locator-callout'; export const NEPAL_CONTEXT_PRESENTATION = 'nepal-context'; @@ -1247,6 +1247,3 @@ export function createBhoteKoshiLocatorLayer({ }, }; } - -const bhoteKoshiLocatorLayer = createBhoteKoshiLocatorLayer(); -export default bhoteKoshiLocatorLayer; diff --git a/src/data/bikeshare.js b/src/data/bikeshare.js index 15c9dc802..6370c790e 100644 --- a/src/data/bikeshare.js +++ b/src/data/bikeshare.js @@ -23,4 +23,3 @@ export { BIKESHARE_SELECTED_OVERLAY_SOURCE_ID, BIKESHARE_SELECTED_OVERLAY_SOURCE_OPTIONS, } from '../layers/bikeshare/index.js'; -export default layer; diff --git a/src/data/cctvFootprint.js b/src/data/cctvFootprint.js index 42a3ad424..93f2b48ed 100644 --- a/src/data/cctvFootprint.js +++ b/src/data/cctvFootprint.js @@ -17,7 +17,7 @@ */ const EARTH_RADIUS_M = 6371000; -export const PLANE_VERT_ASPECT = 16 / 9; +const PLANE_VERT_ASPECT = 16 / 9; export const SUPPORT_KEYS = Object.freeze([ 'bl', 'bm', @@ -128,7 +128,7 @@ export function planeSupportPoints(pose) { * @param {number} capAltM @param {-1|0|1} row @param {{upVert:number}} dims * @returns {number} */ -export function supportAltitude(capAltM, row, dims) { +function supportAltitude(capAltM, row, dims) { return capAltM + row * dims.upVert; } diff --git a/src/data/cctvLod.js b/src/data/cctvLod.js index 44b2e8ba3..834dd964f 100644 --- a/src/data/cctvLod.js +++ b/src/data/cctvLod.js @@ -94,7 +94,7 @@ export function incumbentRankKm( export const CCTV_CARD_CENTER_WEIGHT = 0.5; /** Robust pool-distance percentile used to scale the screen-space term. */ -export const CCTV_CARD_SPREAD_PERCENTILE = 0.9; +const CCTV_CARD_SPREAD_PERCENTILE = 0.9; /** True only when both viewport dimensions are finite positive pixels. */ export function hasFiniteCctvViewport(viewW, viewH) { @@ -172,8 +172,8 @@ export function blendCenterRankKm( // viewport is bucketed into this grid and every occupied cell gets its best // candidate before global rank fills the rest. Owner-tunable together with // the budgets above. -export const CCTV_CARD_GRID_COLS = 5; -export const CCTV_CARD_GRID_ROWS = 4; +const CCTV_CARD_GRID_COLS = 5; +const CCTV_CARD_GRID_ROWS = 4; /** * Screen-space distribution pass (owner round 2, item C). Buckets the diff --git a/src/data/dataCredits.js b/src/data/dataCredits.js index 12dd7682a..aff87aeb7 100644 --- a/src/data/dataCredits.js +++ b/src/data/dataCredits.js @@ -25,11 +25,11 @@ import * as Cesium from 'cesium'; * follows DATA_SOURCES.md (live sources, then bundled snapshots). * @type {{ key: string, html: string }[]} */ -export const OSM_CREDIT = { +const OSM_CREDIT = { key: 'openstreetmap', html: 'Map and place data © OpenStreetMap contributors (ODbL)', }; -export const OPENMAPTILES_CREDIT = { +const OPENMAPTILES_CREDIT = { key: 'openfreemap', html: 'Vector tiles: OpenFreeMap © OpenMapTiles', }; diff --git a/src/data/detectionCohort.js b/src/data/detectionCohort.js index ed9f46c52..95cda50f0 100644 --- a/src/data/detectionCohort.js +++ b/src/data/detectionCohort.js @@ -18,7 +18,7 @@ export function stableIdentityHash(layerId, sourceId) { } /** Negative means a is a better deterministic contender than b. */ -export function compareCohortContenders(a, b) { +function compareCohortContenders(a, b) { const priorityDelta = (Number(b?._cohortPriority) || 0) - (Number(a?._cohortPriority) || 0); if (priorityDelta) return priorityDelta; diff --git a/src/data/detectionPolicy.js b/src/data/detectionPolicy.js index bf47785fd..0105d3beb 100644 --- a/src/data/detectionPolicy.js +++ b/src/data/detectionPolicy.js @@ -5,7 +5,6 @@ * module so density cannot drift into contradictory mode semantics again. */ -export const DENSITY_STOPS = Object.freeze([0, 25, 50, 75, 100]); export const ALLOCATION_ELASTIC = 'ELASTIC'; export const ALLOCATION_WEIGHTED = 'WEIGHTED'; export const ALLOCATION_STRATEGIES = Object.freeze([ @@ -118,7 +117,7 @@ export function detectionHorizontalSector(screenX, viewportWidth) { return 'front'; } -export const VIEW_SCALE_BUDGETS = Object.freeze({ +const VIEW_SCALE_BUDGETS = Object.freeze({ street: Object.freeze({ 0: 9, 25: 23, 50: 45, 75: 68, 100: 90 }), city: Object.freeze({ 0: 7, 25: 18, 50: 35, 75: 53, 100: 70 }), metro: Object.freeze({ 0: 6, 25: 14, 50: 28, 75: 41, 100: 55 }), diff --git a/src/data/directions.js b/src/data/directions.js index ef5565488..8a871bfbf 100644 --- a/src/data/directions.js +++ b/src/data/directions.js @@ -24,7 +24,7 @@ import * as input from './inputOwnership.js'; import { createDirectionsLayer } from '../layers/directions/index.js'; /** The shared scene owners every Directions instance runs on. */ -export const directionsServices = Object.freeze({ +const directionsServices = Object.freeze({ credits, render, sprites, @@ -49,15 +49,11 @@ export { DIRECTIONS_MODES, DIRECTIONS_POINTER_OWNER, DIRECTIONS_ROUTE_COLOR, - DIRECTIONS_STEP_OVERLAY_SOURCE_ID, - DIRECTIONS_STEP_OVERLAY_SOURCE_OPTIONS, - FLIGHT_PROGRESS_MS, POINTER_TOOL_EXITS, STEP_ANCHOR_DEADLINE_MS, STEP_ANCHOR_FAST_ATTEMPTS, STEP_ANCHOR_RETRY_MS, STEP_ANCHOR_SLOW_RETRY_MS, - createDirectionsLayer, createDirectionsStepOverlayEntry, directionsRequestUrl, directionsRowControls, diff --git a/src/data/earthquakes.js b/src/data/earthquakes.js index f003bf712..aac775d92 100644 --- a/src/data/earthquakes.js +++ b/src/data/earthquakes.js @@ -8,4 +8,3 @@ export function createEarthquakesLayer({ } = {}) { return createApplicationEarthquakes({ source, ...options }); } -export default createEarthquakesLayer(); diff --git a/src/data/geoFileImport.js b/src/data/geoFileImport.js index fc6a5e079..cf7037388 100644 --- a/src/data/geoFileImport.js +++ b/src/data/geoFileImport.js @@ -5,9 +5,9 @@ import { createImportedPins } from './geoImportPins.js'; import { msg } from '../i18n/marker.js'; /** Largest file accepted, in bytes. */ -export const MAX_IMPORT_BYTES = 50 * 1024 * 1024; +const MAX_IMPORT_BYTES = 50 * 1024 * 1024; /** Largest total a KMZ may inflate to — a zip bomb stops here. */ -export const MAX_KMZ_INFLATED_BYTES = 120 * 1024 * 1024; +const MAX_KMZ_INFLATED_BYTES = 120 * 1024 * 1024; /** Most files on the globe at once. */ export const MAX_IMPORTED_FILES = 12; /** Most entities one file may create before it is refused. */ @@ -15,9 +15,9 @@ export const MAX_IMPORT_ENTITIES = 20_000; /** Most vertices (points of every line, ring and marker) in one file. */ export const MAX_IMPORT_POSITIONS = 250_000; /** Most normalized style definitions and local image links in one KML/KMZ. */ -export const MAX_KML_LINKS = 2_000; +const MAX_KML_LINKS = 2_000; /** Longest plain-text name kept from a file's features. */ -export const MAX_IMPORT_NAME_LENGTH = 120; +const MAX_IMPORT_NAME_LENGTH = 120; /** What the file picker offers. */ export const GEO_FILE_ACCEPT = '.geojson,.json,.kml,.kmz,.gpx'; diff --git a/src/data/geoImportImages.js b/src/data/geoImportImages.js index 86f8e98e2..71133df26 100644 --- a/src/data/geoImportImages.js +++ b/src/data/geoImportImages.js @@ -1,5 +1,5 @@ // Inspect encoded headers before allowing the browser to decode local images. -export const MAX_IMPORT_IMAGE_PIXELS = 16 * 1024 * 1024; +const MAX_IMPORT_IMAGE_PIXELS = 16 * 1024 * 1024; const MAX_SIDE = 8192; /** Width and height from a supported raster header; no image is decoded. */ diff --git a/src/data/gtfsRealtime.js b/src/data/gtfsRealtime.js index 96d6b28a5..1fe888d4a 100644 --- a/src/data/gtfsRealtime.js +++ b/src/data/gtfsRealtime.js @@ -35,14 +35,14 @@ import { PbfReader } from 'pbf'; /** VehiclePosition.VehicleStopStatus enum → label. */ -export const VEHICLE_STOP_STATUS = Object.freeze({ +const VEHICLE_STOP_STATUS = Object.freeze({ 0: 'INCOMING_AT', 1: 'STOPPED_AT', 2: 'IN_TRANSIT_TO', }); /** VehiclePosition.OccupancyStatus enum → label. */ -export const OCCUPANCY_STATUS = Object.freeze({ +const OCCUPANCY_STATUS = Object.freeze({ 0: 'EMPTY', 1: 'MANY_SEATS_AVAILABLE', 2: 'FEW_SEATS_AVAILABLE', @@ -64,7 +64,6 @@ export const GTFS_MAX_ENTITIES = 50_000; export const GTFS_MAX_STRING_CHARS = 256; /** FeedHeader.Incrementality: only FULL_DATASET carries a complete snapshot. */ export const GTFS_INCREMENTALITY_FULL_DATASET = 0; -export const GTFS_INCREMENTALITY_DIFFERENTIAL = 1; /** * Read a length-delimited string, refusing one that is implausibly long. diff --git a/src/data/localGeojson.js b/src/data/localGeojson.js index 8871cfe39..61f6c3abf 100644 --- a/src/data/localGeojson.js +++ b/src/data/localGeojson.js @@ -1,5 +1,4 @@ import { localGeoJsonServices } from '../app/localGeojsonServices.js'; -export { localGeoJsonServices } from '../app/localGeojsonServices.js'; import { createLocalGeoJsonLayer as createLayer, createLocalInfrastructureOverlayPublisher as createPublisher, diff --git a/src/data/militaryNames.js b/src/data/militaryNames.js index b26ab04e6..d4b4b8846 100644 --- a/src/data/militaryNames.js +++ b/src/data/militaryNames.js @@ -7,7 +7,7 @@ import { createRetryableLoader } from './retryableLoad.js'; export const MILITARY_POINT_CAP = 512; export const MILITARY_LABEL_CAP = 24; -export const MILITARY_NAMES_TIMEOUT_MS = 10_000; +const MILITARY_NAMES_TIMEOUT_MS = 10_000; /** Share a bounded acquisition; failed assets become retryable after cooldown. */ export function createMilitaryNamesLoader({ diff --git a/src/data/radioCountry.js b/src/data/radioCountry.js index 55d9b36a5..1f77d8e98 100644 --- a/src/data/radioCountry.js +++ b/src/data/radioCountry.js @@ -117,8 +117,3 @@ export function normalizeRadioCountryInput(value) { name: canonicalCountryName(code), }); } - -/** Return whether a value is a recognized ISO 3166-1 alpha-2 code. */ -export function isRadioCountryCode(value) { - return typeof value === 'string' && ISO_ALPHA_2_SET.has(value.toUpperCase()); -} diff --git a/src/data/telegeographySubmarineCables.js b/src/data/telegeographySubmarineCables.js index 6aaba700f..6bee7c56d 100644 --- a/src/data/telegeographySubmarineCables.js +++ b/src/data/telegeographySubmarineCables.js @@ -28,4 +28,3 @@ export { createCableReferenceSweepGate, updateCableReferenceStem, } from '../layers/submarineCables/index.js'; -export default createTeleGeographySubmarineCableLayer(); diff --git a/src/data/wind.js b/src/data/wind.js index 38bc10688..59b9aaf5d 100644 --- a/src/data/wind.js +++ b/src/data/wind.js @@ -1,5 +1,4 @@ import { createSourceSlot } from '../sources/sourceSlot.js'; -import { createWindLayer as createLayer } from '../layers/wind/index.js'; import { createWindSource } from '../layers/wind/source.js'; const sourceSlot = createSourceSlot( @@ -8,10 +7,3 @@ const sourceSlot = createSourceSlot( 'Wind source', ); export const configureWindSource = sourceSlot.configure; - -/** Create a wind layer using the configured application source by default. */ -export function createWindLayer(options = {}) { - return createLayer({ ...options, feed: options.feed ?? sourceSlot.source }); -} - -export default createWindLayer(); diff --git a/src/i18n/index.js b/src/i18n/index.js index d5698aa5b..48b63b2f6 100644 --- a/src/i18n/index.js +++ b/src/i18n/index.js @@ -210,7 +210,7 @@ export function formatRelativeTime(value, unit = 'second', options) { } /** Collapse markup whitespace so a message reads the same in HTML and JS. */ -export function normalizeMarkupText(text) { +function normalizeMarkupText(text) { return String(text ?? '') .replace(/\s+/g, ' ') .trim(); diff --git a/src/layers/alpr/records.js b/src/layers/alpr/records.js index 0d751f12d..10cd88062 100644 --- a/src/layers/alpr/records.js +++ b/src/layers/alpr/records.js @@ -1,11 +1,11 @@ import { QUERY_LIMIT, QUERY_SNAP_DEGREES } from './policy.js'; -export function textTag(value) { +function textTag(value) { const t = String(value ?? '').trim(); return t || null; } -export function numTag(value) { +function numTag(value) { // `Number('')` and `Number(' ')` are 0, not NaN — a blank or whitespace-only // tag value must read as missing, never as a due-north bearing. if (value == null) return null; @@ -102,7 +102,7 @@ export function normalizeAlprNode(el) { }; } -export function normalizeDirection(value) { +function normalizeDirection(value) { const degrees = numTag(value); return degrees != null && degrees >= 0 && degrees <= 360 ? degrees % 360 diff --git a/src/layers/alpr/tileRecords.js b/src/layers/alpr/tileRecords.js index 5db9192b3..0368f53fb 100644 --- a/src/layers/alpr/tileRecords.js +++ b/src/layers/alpr/tileRecords.js @@ -3,7 +3,7 @@ import { VectorTile } from '@mapbox/vector-tile'; import { normalizeAlprNode } from './records.js'; /** Map extract attributes without treating an OSM edit date as a field verification. */ -export function normalizeAlprTileFeature(feature) { +function normalizeAlprTileFeature(feature) { const p = feature?.properties || {}; if (feature?.geometry?.type !== 'Point' || p.osmType !== 'node') return null; const [lon, lat] = feature.geometry.coordinates; diff --git a/src/layers/awareness/policy.js b/src/layers/awareness/policy.js index eda5a46e6..22cc8eb98 100644 --- a/src/layers/awareness/policy.js +++ b/src/layers/awareness/policy.js @@ -1,9 +1,6 @@ export const AIRCRAFT_DEPENDENCIES = ['flights', 'military']; -export const DEFERRED_DEPENDENCIES = [ - 'ais-live-vessels', - 'military-installations', -]; +const DEFERRED_DEPENDENCIES = ['ais-live-vessels', 'military-installations']; export const DEPENDENCIES = [ ...AIRCRAFT_DEPENDENCIES, diff --git a/src/layers/bikeshare/policy.js b/src/layers/bikeshare/policy.js index 14f1edf6e..1b9c1579f 100644 --- a/src/layers/bikeshare/policy.js +++ b/src/layers/bikeshare/policy.js @@ -11,7 +11,7 @@ export const BIKESHARE_SELECTED_OVERLAY_SOURCE_OPTIONS = Object.freeze({ // --- Activation / display thresholds --- /** Altitude (m) at which bikeshare layer becomes eligible for display. */ -export const ACTIVATION_ALTITUDE_M = 50000; +const ACTIVATION_ALTITUDE_M = 50000; /** Hysteresis enter threshold — layer activates when camera drops below this. */ diff --git a/src/layers/cctv/policy.js b/src/layers/cctv/policy.js index 96d7985f1..bc369c02a 100644 --- a/src/layers/cctv/policy.js +++ b/src/layers/cctv/policy.js @@ -1,10 +1,4 @@ -export { - FRAME_ENDPOINT, - SOURCE_ENDPOINT, - HEALTH_ENDPOINT, - MEDIA_ENDPOINT, - ACTIVE_FRAME_REFRESH_MS, -} from './sourcePolicy.js'; +export { ACTIVE_FRAME_REFRESH_MS } from './sourcePolicy.js'; import * as Cesium from 'cesium'; import { CCTV_CARD_FETCH_BURST_SPACING_MS } from '../../data/cctvCards.js'; import { CCTV_AMBIENT_CARD_MAX } from '../../data/cctvLod.js'; @@ -90,9 +84,6 @@ export const CCTV_CALIBRATION_STORAGE_KEY_V2 = export const PROJECTION_TEXTURE_SWAP_MS = 1000; -export const PROJECTION_VERT_ASPECT = - PROJECTION_CANVAS_WIDTH / PROJECTION_CANVAS_HEIGHT; - // V2 frustum geometry (design §2a/§6): the far-cap center + corners never sink // below groundAlt + this clearance, so a fabricated pitch (-24°) cannot bury // the monitor plane in the 3D tiles. Exported for the unit suite. diff --git a/src/layers/cctv/sourcePolicy.js b/src/layers/cctv/sourcePolicy.js index 5ce0cd024..3341a4ce9 100644 --- a/src/layers/cctv/sourcePolicy.js +++ b/src/layers/cctv/sourcePolicy.js @@ -1,9 +1,5 @@ export const FRAME_ENDPOINT = '/api/cctv/frame'; -export const SOURCE_ENDPOINT = '/api/cctv/sources'; - -export const HEALTH_ENDPOINT = '/api/cctv/health'; - export const MEDIA_ENDPOINT = '/api/cctv/media'; export const ACTIVE_FRAME_REFRESH_MS = 10000; diff --git a/src/layers/cyclones/labels.js b/src/layers/cyclones/labels.js index e95b5a28c..cb418865f 100644 --- a/src/layers/cyclones/labels.js +++ b/src/layers/cyclones/labels.js @@ -64,7 +64,7 @@ const SHARED_CARD = Object.freeze({ * @param {boolean} selected * @returns {object} */ -export function cycloneStormEntry(storm, selected) { +function cycloneStormEntry(storm, selected) { return { ...SHARED_CARD, ...markerClearance( @@ -92,7 +92,7 @@ export function cycloneStormEntry(storm, selected) { * @param {{tauHours:number,position:object}} point * @returns {object} */ -export function cycloneLeadEntry(stormId, point) { +function cycloneLeadEntry(stormId, point) { return { ...SHARED_CARD, ...markerClearance(CYCLONE_MARKER_PX.forecast), diff --git a/src/layers/flights/policy.js b/src/layers/flights/policy.js index 2c015e6c9..2ecb0a8a4 100644 --- a/src/layers/flights/policy.js +++ b/src/layers/flights/policy.js @@ -1,11 +1,3 @@ -export { - GROUND_FLOOR_CLAMP_RADIUS_KM, - GROUND_FLOOR_WARM_MAX_ALT_M, - POSITION_HISTORY_LIMIT, - LANDED_MISSING_POLL_LIMIT, - MISSING_POLL_LIMIT, - ERROR_BACKOFF_INTERVAL, -} from './recordPolicy.js'; import * as Cesium from 'cesium'; import { EMERGENCY_TINT_CSS } from '../../data/aircraftEmergency.js'; diff --git a/src/layers/installations/policy.js b/src/layers/installations/policy.js index fae021cf3..92da9dd7b 100644 --- a/src/layers/installations/policy.js +++ b/src/layers/installations/policy.js @@ -2,8 +2,6 @@ export const LAYER_ID = 'military-installations'; export const REQUEST_DEBOUNCE_MS = 180; -export const MAX_VIEWPORT_DEGREES = 10; - export const MAX_RENDERED = 700; export const GOOGLE_MILITARY_PLACE_TYPES = new Set(['military_base']); diff --git a/src/layers/localAdsb/feeds.js b/src/layers/localAdsb/feeds.js index 4bb4bced7..5899729af 100644 --- a/src/layers/localAdsb/feeds.js +++ b/src/layers/localAdsb/feeds.js @@ -5,12 +5,12 @@ import { /** Same-origin route served by `server/providers/local-receivers.js`. */ export const LOCAL_RECEIVERS_URL = '/api/local-receivers/aircraft'; -export const LOCAL_RECEIVER_POLL_MS = 1_000; +const LOCAL_RECEIVER_POLL_MS = 1_000; /** * Browser-side deadline for one route request, body included. The server * bounds each upstream read at 2 s; this bounds the browser-to-server leg. */ -export const LOCAL_RECEIVER_REQUEST_TIMEOUT_MS = 5_000; +const LOCAL_RECEIVER_REQUEST_TIMEOUT_MS = 5_000; function abortError() { const error = new Error('Local receiver request aborted'); diff --git a/src/layers/localAdsb/index.js b/src/layers/localAdsb/index.js index fcf54b5d5..4db1e2852 100644 --- a/src/layers/localAdsb/index.js +++ b/src/layers/localAdsb/index.js @@ -36,18 +36,8 @@ import { } from './policy.js'; import { localAdsbStatus } from './status.js'; -export { - LAYER_ID as LOCAL_ADSB_LAYER_ID, - HEARD_BY_RECEIVER, -} from './policy.js'; -export { - localAdsbCardModel, - localAdsbClassLine, - localAdsbIsUat, - localAdsbReceiverLine, - localAdsbTitle, -} from './card.js'; -export { localAdsbClass } from './enrichment.js'; +export { HEARD_BY_RECEIVER } from './policy.js'; +export { localAdsbCardModel, localAdsbReceiverLine } from './card.js'; export { localAdsbStatus } from './status.js'; const FEET_TO_METERS = 0.3048; diff --git a/src/layers/localAdsb/motion.js b/src/layers/localAdsb/motion.js index 279bef6d3..084f3aa7e 100644 --- a/src/layers/localAdsb/motion.js +++ b/src/layers/localAdsb/motion.js @@ -26,7 +26,7 @@ import { export const LOCAL_ADSB_HISTORY_MS = 10 * 60_000; export const LOCAL_ADSB_HISTORY_POINTS = 600; /** A display correction larger than this snaps instead of sliding. */ -export const LOCAL_ADSB_SNAP_M = 3_000; +const LOCAL_ADSB_SNAP_M = 3_000; /** Chord course uses the newest fix at least this much older than the anchor. */ const CHORD_BASELINE_MS = 2_000; /** Turn rate is estimated over this window of heard fixes. */ diff --git a/src/layers/localAdsb/status.js b/src/layers/localAdsb/status.js index d264c5e15..2e084cffb 100644 --- a/src/layers/localAdsb/status.js +++ b/src/layers/localAdsb/status.js @@ -24,7 +24,7 @@ function plural(count, one, many) { * @param {object[]} feeds Feed statuses from the route. * @returns {string} */ -export function describeFeedProblems(feeds) { +function describeFeedProblems(feeds) { const byStatus = new Map(); for (const feed of feeds) { if (feed?.status === 'live') continue; @@ -41,7 +41,7 @@ export function describeFeedProblems(feeds) { } /** Whether the server reported at least one configured feed. */ -export function feedsConfigured(feedState) { +function feedsConfigured(feedState) { return Boolean( feedState?.configured === true && Array.isArray(feedState.feeds) && diff --git a/src/layers/military/policy.js b/src/layers/military/policy.js index f375e5419..bb1d89127 100644 --- a/src/layers/military/policy.js +++ b/src/layers/military/policy.js @@ -1,10 +1,3 @@ -export { - GROUND_FLOOR_WARM_MAX_ALT_M, - POSITION_HISTORY_LIMIT, - LANDED_MISSING_POLL_LIMIT, - MISSING_POLL_LIMIT, - ERROR_BACKOFF_INTERVAL, -} from './recordPolicy.js'; import * as Cesium from 'cesium'; import { EMERGENCY_TINT_CSS } from '../../data/aircraftEmergency.js'; diff --git a/src/layers/radio/policy.js b/src/layers/radio/policy.js index 85777ed74..0d4ad174a 100644 --- a/src/layers/radio/policy.js +++ b/src/layers/radio/policy.js @@ -10,7 +10,7 @@ export const MARKER_LIFT_M = 2.5; export const SELECTED_LIFT_M = 5; -export const RADIO_PICK_TOLERANCE_PX = 8; +const RADIO_PICK_TOLERANCE_PX = 8; export const RADIO_TUNER_DIRECTORY_LIMIT = 750; diff --git a/src/layers/recentImagery/model.js b/src/layers/recentImagery/model.js index 406dc3e19..3fbcf1123 100644 --- a/src/layers/recentImagery/model.js +++ b/src/layers/recentImagery/model.js @@ -300,7 +300,7 @@ export function hhmm(iso) { } /** Candidate key: `PRODUCT:YYYY-MM-DD`. */ -export function candidateKey(product, day) { +function candidateKey(product, day) { return `${product}:${day}`; } diff --git a/src/layers/satellites/policy.js b/src/layers/satellites/policy.js index 656374fc6..e7ea5456d 100644 --- a/src/layers/satellites/policy.js +++ b/src/layers/satellites/policy.js @@ -94,9 +94,9 @@ export const TRACK_VIEW_FROM_HIGH_SCALE = 4; * attribute, so classification costs nothing per frame. */ -export const POINT_OUTLINE = Cesium.Color.WHITE.withAlpha(0.3); +const POINT_OUTLINE = Cesium.Color.WHITE.withAlpha(0.3); -export const _classColor = (group) => +const _classColor = (group) => Cesium.Color.fromCssColorString(satelliteClassColor(group)); export const POINT_STYLES = { diff --git a/src/layers/streetLevel/photoViewer.js b/src/layers/streetLevel/photoViewer.js index c083879db..0ca4e3eb2 100644 --- a/src/layers/streetLevel/photoViewer.js +++ b/src/layers/streetLevel/photoViewer.js @@ -17,18 +17,18 @@ const EXPANDED_SELECTORS = [ ]; /** A photo download (tens of kilobytes to a few megabytes). */ -export const IMAGE_TIMEOUT_MS = 15_000; +const IMAGE_TIMEOUT_MS = 15_000; /** A picture file or decoded side past these is refused (a photo is a few MB). */ export const MAX_IMAGE_BYTES = 48 * 1024 * 1024; export const MAX_IMAGE_SIDE = 16_384; /** 64 MP: 256 MB once decoded to RGBA. A 16,384 px square is four times that. */ -export const MAX_IMAGE_PIXELS = 8_192 * 8_192; +const MAX_IMAGE_PIXELS = 8_192 * 8_192; /** Where in a file to look for its dimensions (a JPEG's EXIF comes first). */ const HEADER_BYTES = 256 * 1024; /** Whether a picture this size is past what the viewer decodes. */ -export function tooManyPixels(width, height) { +function tooManyPixels(width, height) { return ( width > MAX_IMAGE_SIDE || height > MAX_IMAGE_SIDE || @@ -37,7 +37,7 @@ export function tooManyPixels(width, height) { } /** Panorama texture widths: enough for a docked viewer, and for an expanded one. */ -export const PANO_MID_SIZE = Object.freeze([2048, 1024]); +const PANO_MID_SIZE = Object.freeze([2048, 1024]); export const PANO_FULL_SIZE = Object.freeze([4096, 2048]); /** @@ -155,7 +155,7 @@ async function loadBitmap(url, timeoutMs, resize, failure, signal) { * A picture refused for its size (`tooLarge`): the viewer will not load * that file another way, an having no size limit at all. */ -export function oversized(failure) { +function oversized(failure) { return Object.assign(new Error(failure), { tooLarge: true }); } diff --git a/src/layers/streetLevel/providers/kartaview/policy.js b/src/layers/streetLevel/providers/kartaview/policy.js index 939e0ec1c..b4533c1b5 100644 --- a/src/layers/streetLevel/providers/kartaview/policy.js +++ b/src/layers/streetLevel/providers/kartaview/policy.js @@ -14,7 +14,7 @@ export const KARTAVIEW_LABEL = 'KARTAVIEW'; */ export const KARTAVIEW_API_HOST = 'https://api.kartaview.org'; /** KartaView's image proxy: resizes a photo given its base64url address. */ -export const KARTAVIEW_CDN_HOST = 'https://cdn.kartaview.org'; +const KARTAVIEW_CDN_HOST = 'https://cdn.kartaview.org'; /** Stable id prefixes for picked primitives; every one starts with `kv:`. */ export const PICK_PREFIX = Object.freeze({ @@ -140,8 +140,6 @@ export const MASK_SLOW_MESSAGE = msg( export const FILTERED_OUT_MESSAGE = msg( 'None of KartaView’s photos here pass the imagery filter', ); -/** Under the SINCE slider while KartaView is lit. */ -export const SINCE_NOTE = '(by upload date)'; /** The UTC calendar day of `ms`, as KartaView's startDate reads it. */ function utcDay(ms) { diff --git a/src/layers/streetLevel/providers/panoramax/index.js b/src/layers/streetLevel/providers/panoramax/index.js index 4dfa023ef..e855c9c72 100644 --- a/src/layers/streetLevel/providers/panoramax/index.js +++ b/src/layers/streetLevel/providers/panoramax/index.js @@ -41,8 +41,6 @@ import { normalizeItem, } from './policy.js'; -export { PANORAMAX_PROVIDER_ID } from './policy.js'; - /** * A tile's bytes decoded by `decode`; a body that is not a vector tile * (an HTML error page, a cut-off answer) reads as such, not as a parser's diff --git a/src/layers/streetLevel/providers/panoramax/policy.js b/src/layers/streetLevel/providers/panoramax/policy.js index e9a51c10e..670772c6a 100644 --- a/src/layers/streetLevel/providers/panoramax/policy.js +++ b/src/layers/streetLevel/providers/panoramax/policy.js @@ -15,7 +15,7 @@ export const PANORAMAX_LABEL = 'PANORAMAX'; */ export const PANORAMAX_API = 'https://api.panoramax.xyz/api'; /** The meta-catalogue's web viewer, where the caption link goes. */ -export const PANORAMAX_VIEWER = 'https://api.panoramax.xyz/'; +const PANORAMAX_VIEWER = 'https://api.panoramax.xyz/'; /** Stable id prefixes for picked primitives; every one starts with `pnx:`. */ export const PICK_PREFIX = Object.freeze({ diff --git a/src/layers/streetLevel/registry.js b/src/layers/streetLevel/registry.js index 6770cd133..5e51929a6 100644 --- a/src/layers/streetLevel/registry.js +++ b/src/layers/streetLevel/registry.js @@ -1,6 +1,6 @@ import { encodeIdSwitches } from '../../data/idSwitches.js'; -export const IMAGERY_FILTERS = Object.freeze(['pano', 'since']); +const IMAGERY_FILTERS = Object.freeze(['pano', 'since']); export function filtersOf(provider) { return provider?.filters ?? IMAGERY_FILTERS; diff --git a/src/layers/streetLevel/sequenceCones.js b/src/layers/streetLevel/sequenceCones.js index b18f2cc57..b6c068d49 100644 --- a/src/layers/streetLevel/sequenceCones.js +++ b/src/layers/streetLevel/sequenceCones.js @@ -5,8 +5,8 @@ import { createHorizonCull, metresBetween } from './view.js'; import { msg } from '../../i18n/index.js'; /** Cones closer than this to the previous kept one are dropped. */ -export const IMAGE_CONE_MIN_SPACING_M = 3; -export const IMAGE_CONE_SIZE_PX = 26; +const IMAGE_CONE_MIN_SPACING_M = 3; +const IMAGE_CONE_SIZE_PX = 26; /** * @typedef {object} ConeImage One image of a sequence, as the cones draw it. diff --git a/src/layers/streetLevel/tileCache.js b/src/layers/streetLevel/tileCache.js index d6c0a91e5..6ee0897ef 100644 --- a/src/layers/streetLevel/tileCache.js +++ b/src/layers/streetLevel/tileCache.js @@ -1,6 +1,6 @@ /** Decoded coverage tiles kept, and for how long. */ -export const TILE_CACHE_SIZE = 24; -export const TILE_CACHE_TTL_MS = 10 * 60_000; +const TILE_CACHE_SIZE = 24; +const TILE_CACHE_TTL_MS = 10 * 60_000; /** Least-recently-used map: reading an entry renews it. */ export function createLru(limit) { diff --git a/src/layers/streetLevel/uiState.js b/src/layers/streetLevel/uiState.js index 577bb8ae1..04c6ed6c4 100644 --- a/src/layers/streetLevel/uiState.js +++ b/src/layers/streetLevel/uiState.js @@ -64,7 +64,7 @@ export function keyAlternatives(providers) { * The filter controls a switched-on provider honours (both when none is on). * @param {Array} providers */ -export function filterControls(providers) { +function filterControls(providers) { const active = providers.filter((p) => p.on); const honoured = (key) => active.length === 0 || @@ -78,7 +78,7 @@ export function filterControls(providers) { * note needs no provider name. * @param {Array} providers */ -export function sinceNote(providers) { +function sinceNote(providers) { return providers.some((p) => p.on && p.sinceBasis === 'upload') ? t('(by upload date)') : null; diff --git a/src/layers/submarineCables/geometry.js b/src/layers/submarineCables/geometry.js index 08366dba4..b4b434710 100644 --- a/src/layers/submarineCables/geometry.js +++ b/src/layers/submarineCables/geometry.js @@ -33,7 +33,7 @@ export function featureReference(feature) { }; } -export function coordsFromProperty(value) { +function coordsFromProperty(value) { if (!Array.isArray(value) || value.length < 2) return null; const lon = Number(value[0]); const lat = Number(value[1]); @@ -41,7 +41,7 @@ export function coordsFromProperty(value) { return [lon, lat]; } -export function coordsFromPoint(value) { +function coordsFromPoint(value) { if (!Array.isArray(value) || value.length < 2) return null; const lon = Number(value[0]); const lat = Number(value[1]); @@ -49,7 +49,7 @@ export function coordsFromPoint(value) { return [lon, lat]; } -export function collectLonLat(value, out) { +function collectLonLat(value, out) { if (!Array.isArray(value)) return; if (typeof value[0] === 'number' && typeof value[1] === 'number') { const lon = Number(value[0]); diff --git a/src/layers/traffic/footprint.js b/src/layers/traffic/footprint.js index d75947941..9a241d997 100644 --- a/src/layers/traffic/footprint.js +++ b/src/layers/traffic/footprint.js @@ -1,9 +1,9 @@ import * as Cesium from 'cesium'; import { tilesForBounds, tileToBBox } from '../../data/tomtomTiles.js'; -export const RETICLE_RANGE_KM = 10; -export const RETICLE_TILE_CAP = 16; -export const tileKey = (tile) => `${tile.z}/${tile.x}/${tile.y}`; +const RETICLE_RANGE_KM = 10; +const RETICLE_TILE_CAP = 16; +const tileKey = (tile) => `${tile.z}/${tile.x}/${tile.y}`; const cross = (a, b, c) => (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]); diff --git a/src/layers/traffic/policy.js b/src/layers/traffic/policy.js index 81b01f299..c20b15e41 100644 --- a/src/layers/traffic/policy.js +++ b/src/layers/traffic/policy.js @@ -175,13 +175,3 @@ export const STYLED_MIN_BASE_PX = 5; /** @const {number} Maximum tile cache entries before LRU eviction */ export const TILE_CACHE_MAX_ENTRIES = 64; - -/** - * Milliseconds the first dot paint will wait for flow data. Cached flow - * settles within this window (decode cache, 120 s TTL) and renders fully - * colored; a cold tile fetch loses the race, dots paint immediately in - * white, and recolorDotsInPlace applies the colors when flow arrives — - * field-test round 1's "takes forever to load" was the sequential wait. - */ - -export const FLOW_RENDER_RACE_MS = 250; diff --git a/src/layers/transit/policy.js b/src/layers/transit/policy.js index 2fb5101ce..010a2efc2 100644 --- a/src/layers/transit/policy.js +++ b/src/layers/transit/policy.js @@ -99,7 +99,7 @@ export const VISIBILITY_REFRESH_MS = 250; * on screen. Generous on purpose: a vehicle that crosses the edge mid-glide * should already have been moving, not pop into motion at the border. */ -export const VIEW_MARGIN_RATIO = 0.6; +const VIEW_MARGIN_RATIO = 0.6; /** * Whether a vehicle is close enough to the camera's look-at area to be worth @@ -516,10 +516,10 @@ export function createTransitSelectedOverlayEntry(key, position, copy, mode) { } /** Modes that ordinarily run below the street, drawn on the surface above themselves. */ -export const UNDERGROUND_MODES = new Set(['subway']); +const UNDERGROUND_MODES = new Set(['subway']); /** The mode in plain words, for a card a person reads rather than scans. */ -export const TRANSIT_MODE_WORD = Object.freeze({ +const TRANSIT_MODE_WORD = Object.freeze({ bus: msg('Bus'), tram: msg('Tram'), subway: msg('Subway'), @@ -529,7 +529,7 @@ export const TRANSIT_MODE_WORD = Object.freeze({ }); /** Short, fixed-width mode word for the detection card's class field. */ -export const TRANSIT_MODE_ABBR = Object.freeze({ +const TRANSIT_MODE_ABBR = Object.freeze({ bus: 'BUS', tram: 'TRAM', subway: 'METRO', @@ -588,7 +588,7 @@ export function transitDetectionId(record, feed = null) { } /** Longest route text the single-line detection label can carry. */ -export const DETECTION_ID_MAX_CHARS = 14; +const DETECTION_ID_MAX_CHARS = 14; /** * Cut long route text with an ellipsis, so a reader can see it was cut. @@ -600,7 +600,7 @@ export const DETECTION_ID_MAX_CHARS = 14; * @param {string} text * @returns {string} Uppercase, at most DETECTION_ID_MAX_CHARS characters. */ -export function elideRouteText(text) { +function elideRouteText(text) { const value = String(text || '').toUpperCase(); if (value.length <= DETECTION_ID_MAX_CHARS) return value; return `${value.slice(0, DETECTION_ID_MAX_CHARS - 1)}\u2026`; @@ -659,6 +659,6 @@ export function transitDetectionMetric( * @param {number} [nowMs] * @returns {string} */ -export function transitVehicleState(entry, nowMs = Date.now()) { +function transitVehicleState(entry, nowMs = Date.now()) { return displayMotion(entry, nowMs).word; } diff --git a/src/layers/vessels/policy.js b/src/layers/vessels/policy.js index 75e5cdd67..9ab06fbb8 100644 --- a/src/layers/vessels/policy.js +++ b/src/layers/vessels/policy.js @@ -1,8 +1,3 @@ -export { - PARTIAL_RETENTION_MS, - SELECTED_PIN_REFRESHES, - AIS_FIRST_CONNECT_LABEL, -} from './recordPolicy.js'; import { VESSEL_LABEL_GRID_PX } from '../../data/vesselLabels.js'; import { msg } from '../../i18n/index.js'; diff --git a/src/layers/weather/source.js b/src/layers/weather/source.js index cf004476f..5386bfad7 100644 --- a/src/layers/weather/source.js +++ b/src/layers/weather/source.js @@ -4,7 +4,7 @@ import { readResponseJsonCapped } from '../../sources/httpBody.js'; // the weather layer translates them where it shows them (t / translateKnown). const msg = (text) => text; -export const WEATHER_PRODUCTS = Object.freeze([ +const WEATHER_PRODUCTS = Object.freeze([ 'radar', 'clouds', 'clouds-regional', diff --git a/src/layers/wind/gpuRendering.js b/src/layers/wind/gpuRendering.js index 27d71dcd5..0873fa63b 100644 --- a/src/layers/wind/gpuRendering.js +++ b/src/layers/wind/gpuRendering.js @@ -6,8 +6,8 @@ import { WIND_NARROW_PATH_LIMIT, } from './streamlines.js'; -export const WIND_FADE_LOW_METERS = 15000; -export const WIND_FADE_HIGH_METERS = 60000; +const WIND_FADE_LOW_METERS = 15000; +const WIND_FADE_HIGH_METERS = 60000; // Original material: a persistent fine curve plus a moving, tapered highlight. // The integer part of s identifies a path; its fractional part follows the wind. diff --git a/src/locationStatus.js b/src/locationStatus.js index 1d90582c9..2c9e406f3 100644 --- a/src/locationStatus.js +++ b/src/locationStatus.js @@ -64,5 +64,3 @@ export function locationMiniStatus({ return { ...EMPTY }; } - -export default locationMiniStatus; diff --git a/src/maps/imageryComparison.js b/src/maps/imageryComparison.js index 5586bd86e..cf7d266ae 100644 --- a/src/maps/imageryComparison.js +++ b/src/maps/imageryComparison.js @@ -1,5 +1,5 @@ /** Basemap that before/after imagery comparisons are read against. */ -export const IMAGERY_COMPARISON_STACK_ID = 'esri-imagery'; +const IMAGERY_COMPARISON_STACK_ID = 'esri-imagery'; const SWITCH_POLICIES = new Set(['preserve', 'esri', 'esri-if-photoreal']); const leases = new WeakMap(); diff --git a/src/maps/terrainRetry.js b/src/maps/terrainRetry.js index 26fc95576..6cf0fe2af 100644 --- a/src/maps/terrainRetry.js +++ b/src/maps/terrainRetry.js @@ -24,7 +24,7 @@ */ export const TERRAIN_RETRY_ATTEMPTS = 3; -export const TERRAIN_RETRY_STATUSES = Object.freeze([429, 502, 503, 504]); +const TERRAIN_RETRY_STATUSES = Object.freeze([429, 502, 503, 504]); export const TERRAIN_RETRY_BASE_MS = 750; export const TERRAIN_RETRY_MAX_MS = 15_000; export const TERRAIN_RETRY_SPREAD_MS = 1_500; diff --git a/src/scenes/recipes.js b/src/scenes/recipes.js index 191bb1ac1..ba3940a57 100644 --- a/src/scenes/recipes.js +++ b/src/scenes/recipes.js @@ -994,7 +994,7 @@ const PUBLIC_SCENE_RECIPES = [ ]; /** Build the recipe list without mutating stored user-authored projects. */ -export function createSceneRecipes({ localDemoRecipes = EVENT_RECIPES } = {}) { +function createSceneRecipes({ localDemoRecipes = EVENT_RECIPES } = {}) { return [...localDemoRecipes, ...PUBLIC_SCENE_RECIPES]; } @@ -1004,7 +1004,7 @@ export const SCENE_APPEND_RECIPES = BHOTE_KOSHI_NEPAL_APPEND_RECIPE ? [BHOTE_KOSHI_NEPAL_APPEND_RECIPE] : []; -export function getSceneRecipeById(id) { +function getSceneRecipeById(id) { return SCENE_RECIPES.find((recipe) => recipe.id === id) || null; } diff --git a/src/sdr/controller.js b/src/sdr/controller.js index 50fec4a38..019b768a1 100644 --- a/src/sdr/controller.js +++ b/src/sdr/controller.js @@ -38,12 +38,12 @@ const READ_PIPELINE_DEPTH = 4; const RATE_WINDOW_MS = 1_000; // A bulk read that has not completed in this long is treated as a stalled // USB transfer: the session is torn down instead of waiting forever. -export const SDR_READ_STALL_MS = 5_000; +const SDR_READ_STALL_MS = 5_000; // Decoder admission. Every block sent to the worker is numbered and must be // acknowledged; past these bounds the receiver stops with an overload error // rather than queueing raw IQ the decoder cannot keep up with. Eight 16 ms // blocks is 128 ms of input, about forty times the measured decode cost. -export const SDR_WORKER_ADMISSION = Object.freeze({ +const SDR_WORKER_ADMISSION = Object.freeze({ maxBlocks: 8, maxBytes: 8 * 65_536, maxAgeMs: 1_000, @@ -66,7 +66,7 @@ const DECODER_WINDOW_SAMPLES = 256; // raw USB device is closed directly, which aborts any pending transfer. The // raw close is bounded by the same deadline, so a teardown settles within // twice this even when `USBDevice.close()` itself never settles. -export const SDR_CLOSE_TIMEOUT_MS = 2_000; +const SDR_CLOSE_TIMEOUT_MS = 2_000; /** Tuning presets per receiver mode; gain is a separate per-mode setting. */ export const SDR_RECEIVER_PRESETS = Object.freeze({ @@ -1912,9 +1912,3 @@ function formatMhz(hz) { function positiveModulo(value, divisor) { return ((value % divisor) + divisor) % divisor; } - -export const SDR_FM_BAND = Object.freeze({ - minHz: FM_MIN_HZ, - maxHz: FM_MAX_HZ, -}); -export const SDR_ADSB_FREQUENCY_HZ = ADSB_FREQUENCY_HZ; diff --git a/src/sdr/gain.js b/src/sdr/gain.js index 761edf013..94d525fe2 100644 --- a/src/sdr/gain.js +++ b/src/sdr/gain.js @@ -10,7 +10,7 @@ * about 1 msg/s and 28.0 dB about 9 msg/s on the same antenna. */ -export const SDR_GAIN_AUTO = 'auto'; +const SDR_GAIN_AUTO = 'auto'; /** R820T tuner gain steps in dB, as listed by librtlsdr. */ export const R820T_GAIN_STEPS_DB = Object.freeze([ diff --git a/src/sky/stars.js b/src/sky/stars.js index 9fd5aa698..f4139c3c5 100644 --- a/src/sky/stars.js +++ b/src/sky/stars.js @@ -18,7 +18,7 @@ export const STAR_MAX_SIGMA_PX = 1.35; /** Peak opacity of the faintest star. */ export const STAR_MIN_ALPHA = 0.22; /** B−V for stars the catalogue gives no colour (a solar-type white). */ -export const STAR_DEFAULT_BV = 0.6; +const STAR_DEFAULT_BV = 0.6; /** Floats per star in the vertex buffer: xyz, σ, rgb, alpha. */ export const STAR_VERTEX_FLOATS = 8; @@ -76,7 +76,7 @@ export function bvToTemperature(bv) { } /** Black-body colour of a temperature, sRGB 0..1 (Helland's fit). */ -export function temperatureToRgb(kelvin) { +function temperatureToRgb(kelvin) { const t = clamp(kelvin, 1000, 40000) / 100; const red = t <= 66 ? 255 : 329.698727446 * (t - 60) ** -0.1332047592; const green = diff --git a/src/sources/adsbRecords.js b/src/sources/adsbRecords.js index 9c0082700..814da60b2 100644 --- a/src/sources/adsbRecords.js +++ b/src/sources/adsbRecords.js @@ -41,12 +41,12 @@ const CATEGORY_PATTERN = /^[A-D][0-7]$/; /** A previous accepted fix older than this is no reference for a new one. */ export const LOCAL_ADSB_REFERENCE_MAX_AGE_MS = 10 * 60_000; /** Speed limit (kt) for an aircraft that has not reported a ground speed. */ -export const LOCAL_ADSB_UNKNOWN_SPEED_LIMIT_KT = 1_000; +const LOCAL_ADSB_UNKNOWN_SPEED_LIMIT_KT = 1_000; /** Tighter limit (kt) for light aircraft and rotorcraft without a speed. */ -export const LOCAL_ADSB_SLOW_CATEGORY_LIMIT_KT = 350; +const LOCAL_ADSB_SLOW_CATEGORY_LIMIT_KT = 350; /** Reported ground speed is multiplied by this, plus the margin below. */ -export const LOCAL_ADSB_SPEED_FACTOR = 1.5; -export const LOCAL_ADSB_SPEED_MARGIN_KT = 50; +const LOCAL_ADSB_SPEED_FACTOR = 1.5; +const LOCAL_ADSB_SPEED_MARGIN_KT = 50; /** Distance always allowed between two fixes (CPR and reception error). */ export const LOCAL_ADSB_POSITION_MARGIN_M = 500; /** diff --git a/src/sources/featureGeometry.js b/src/sources/featureGeometry.js index 429dbe826..a18d5f22f 100644 --- a/src/sources/featureGeometry.js +++ b/src/sources/featureGeometry.js @@ -77,7 +77,7 @@ export function simplifyRing(ring, tolM) { return out.length >= 4 ? out : ring; } -export function douglasPeucker(points, tol) { +function douglasPeucker(points, tol) { const n = points.length; if (n < 3) return points.slice(); // Iterative, index-based (no per-call array slicing / recursion), so a large @@ -113,7 +113,7 @@ export function douglasPeucker(points, tol) { return out; } -export function perpDistance(p, a, b) { +function perpDistance(p, a, b) { const dx = b[0] - a[0]; const dy = b[1] - a[1]; const len2 = dx * dx + dy * dy; @@ -149,7 +149,7 @@ export function stitchRing(ways) { return endpointGapM(largest) <= allow ? largest : []; } -export function largestByArea(components) { +function largestByArea(components) { let best = components[0]; let bestArea = approximateAreaM2(best); for (let i = 1; i < components.length; i += 1) { @@ -162,7 +162,7 @@ export function largestByArea(components) { return best; } -export function ringSpanM(chain) { +function ringSpanM(chain) { let minLat = Infinity; let maxLat = -Infinity; let minLon = Infinity; @@ -176,7 +176,7 @@ export function ringSpanM(chain) { return approximateDistanceM(minLat, minLon, maxLat, maxLon); } -export function buildRingComponents(ways) { +function buildRingComponents(ways) { const same = (a, b) => Math.abs(a.lon - b.lon) < 1e-7 && Math.abs(a.lat - b.lat) < 1e-7; const remaining = ways.map((w) => w.slice()); @@ -207,7 +207,7 @@ export function buildRingComponents(ways) { return components; } -export function endpointGapM(chain) { +function endpointGapM(chain) { if (!chain || chain.length < 2) return Infinity; const a = chain[0]; const b = chain[chain.length - 1]; diff --git a/src/sources/openFreeMap.js b/src/sources/openFreeMap.js index 274c2ace7..00c288a8b 100644 --- a/src/sources/openFreeMap.js +++ b/src/sources/openFreeMap.js @@ -24,7 +24,7 @@ const ROAD_TYPES = Object.freeze({ * OpenMapTiles folds private access into `no`. Tunnels cannot be placed on * the surface above them; ramps/bridges remain valid only on a motor road. */ -export function isDrivableOpenMapRoad(properties = {}) { +function isDrivableOpenMapRoad(properties = {}) { return ( Object.hasOwn(ROAD_TYPES, properties.class) && !properties.subclass && diff --git a/src/sources/transitHistoryStore.js b/src/sources/transitHistoryStore.js index 9fe29f59d..cf82835c1 100644 --- a/src/sources/transitHistoryStore.js +++ b/src/sources/transitHistoryStore.js @@ -1,7 +1,7 @@ import { validTransitIdentifier } from './transitHistory.js'; export { validTransitIdentifier } from './transitHistory.js'; /** Bounded, process-local operator position history. No upstream or scene access. */ -export const TRANSIT_HISTORY_LIMITS = Object.freeze({ +const TRANSIT_HISTORY_LIMITS = Object.freeze({ retentionMs: 900000, fixes: 128, feedBytes: 16 * 1024 * 1024, diff --git a/src/testSupport/kartaViewFakes.mjs b/src/testSupport/kartaViewFakes.mjs index a2e6452f6..326ba9d77 100644 --- a/src/testSupport/kartaViewFakes.mjs +++ b/src/testSupport/kartaViewFakes.mjs @@ -1,7 +1,7 @@ // DOM stand-ins for the KartaView viewer's tests (no browser in Node). /** A DOM element with what the viewer touches: children, events, attributes. */ -export function fakeElement(tag) { +function fakeElement(tag) { const listeners = new Map(); const element = { tagName: tag.toUpperCase(), diff --git a/src/tools/queries/panelRequest.js b/src/tools/queries/panelRequest.js index 3864c9889..51ecd6406 100644 --- a/src/tools/queries/panelRequest.js +++ b/src/tools/queries/panelRequest.js @@ -55,7 +55,7 @@ const COMPRESS_MIN_BYTES = 1024; * rather than fail; past `PANEL_QUEUED_REQUESTS` waiting, they are refused. */ export const PANEL_CONCURRENT_REQUESTS = 6; -export const PANEL_QUEUED_REQUESTS = 256; +const PANEL_QUEUED_REQUESTS = 256; /** * Each server's panel state: bodies too large for one call, kept for the diff --git a/src/tools/queries/share.js b/src/tools/queries/share.js index 0c59ca7f0..5284e8a06 100644 --- a/src/tools/queries/share.js +++ b/src/tools/queries/share.js @@ -7,7 +7,7 @@ import { AREA_SCHEMA, resolveArea } from '../area.js'; import { cameraForArea, cameraLookingAt } from '../views.js'; /** Tool arguments that describe a view: an area to frame, or a camera. */ -export const VIEW_ARGUMENTS = Object.freeze({ +const VIEW_ARGUMENTS = Object.freeze({ view: Object.freeze({ type: 'object', description: diff --git a/src/ui/cameraOrientationControls.js b/src/ui/cameraOrientationControls.js index 0fb67f1d4..d10bacf2f 100644 --- a/src/ui/cameraOrientationControls.js +++ b/src/ui/cameraOrientationControls.js @@ -107,7 +107,7 @@ export function readCameraTargetFrame(viewer) { } /** Apply an orbit frame, then return the camera to Cesium's fixed-world frame. */ -export function setCameraTargetFrame(viewer, frame) { +function setCameraTargetFrame(viewer, frame) { const camera = viewer?.camera; if (!camera || !frame?.target || !Number.isFinite(frame.range)) return false; try { @@ -156,7 +156,7 @@ export function setCameraTargetFrame(viewer, frame) { * * @param {{pitch: number}|null} frame From {@link readCameraTargetFrame}. */ -export function frameIsTilted(frame) { +function frameIsTilted(frame) { const pitch = Number(frame?.pitch); return Number.isFinite(pitch) ? pitch > OBLIQUE_THRESHOLD : false; } diff --git a/src/ui/cctvFrames.js b/src/ui/cctvFrames.js index ac6687f54..33818984d 100644 --- a/src/ui/cctvFrames.js +++ b/src/ui/cctvFrames.js @@ -46,7 +46,7 @@ const SOURCE_LABELS = Object.freeze({ }); /** "SNAPSHOT" / "СНИМОК"; unknown codes (HLS, MJPEG…) stay as uppercase codes. */ -export function cctvSourceKindLabel(kind) { +function cctvSourceKindLabel(kind) { const key = String(kind || 'unknown').toLowerCase(); return SOURCE_KIND_LABELS[key] ? t(SOURCE_KIND_LABELS[key]) diff --git a/src/ui/statusText.js b/src/ui/statusText.js index b0899d55e..677c9b886 100644 --- a/src/ui/statusText.js +++ b/src/ui/statusText.js @@ -218,7 +218,7 @@ function statusPatternText(piece) { } /** One piece of a status line in the UI language, or as it came. */ -export function translateStatusPiece(piece) { +function translateStatusPiece(piece) { const known = translateKnown(piece); if (known !== piece) return known; return statusPatternText(piece) ?? piece; diff --git a/src/voice/deixis.js b/src/voice/deixis.js index d68fcd61a..25d7939eb 100644 --- a/src/voice/deixis.js +++ b/src/voice/deixis.js @@ -18,7 +18,7 @@ import { finiteOrNull } from './pointerContext.js'; -export const POINTER_SENTINEL = 'pointer'; +const POINTER_SENTINEL = 'pointer'; const TRACKABLE_LAYERS = new Set([ 'flights', diff --git a/src/voice/narration.js b/src/voice/narration.js index a91dbbe2b..ae97931f7 100644 --- a/src/voice/narration.js +++ b/src/voice/narration.js @@ -4,7 +4,7 @@ import { progressLine } from './speech.js'; * Timing policy for filling silence while tools run. Tuned against the turn * spans' `max_silence_ms`. */ -export const NARRATION_TIMING = Object.freeze({ +const NARRATION_TIMING = Object.freeze({ /** Start the earcon when nothing is audible this long after end of speech. */ earconAfterMs: 700, /** First spoken progress line when a tool is still running after this. */ diff --git a/src/voice/pointerContext.js b/src/voice/pointerContext.js index 3f9d09b6d..590e33a52 100644 --- a/src/voice/pointerContext.js +++ b/src/voice/pointerContext.js @@ -20,15 +20,15 @@ */ /** Movement below this many CSS pixels is still the same dwell. */ -export const POINTER_DWELL_PX = 12; +const POINTER_DWELL_PX = 12; /** Stillness that makes a dwell point. */ export const POINTER_DWELL_MS = 250; /** A resting pointer stays a referent this long after it last moved. */ export const POINTER_FRESH_MS = 30000; /** Pixels from the canvas edge that count as "parked", never a referent. */ -export const POINTER_EDGE_PX = 8; +const POINTER_EDGE_PX = 8; /** A dwell pick is reused by a snapshot only while this recent (contacts move). */ -export const POINTER_DWELL_REUSE_MS = 1000; +const POINTER_DWELL_REUSE_MS = 1000; /** * Open mic has no gesture, so the pointer only counts when it moved or * settled on the map this recently: a cursor resting there for minutes is @@ -76,7 +76,7 @@ export function cameraPoseKey(viewer) { } /** Bound and de-control a label before it goes to the model or the card. */ -export function pointerLabel(value, max = LABEL_MAX) { +function pointerLabel(value, max = LABEL_MAX) { const clean = String(value ?? '') .replace(/[\p{Cc}\p{Cf}]+/gu, ' ') .replace(/\s+/g, ' ') diff --git a/src/voice/pointerPick.js b/src/voice/pointerPick.js index 33ccab9cb..e74671eda 100644 --- a/src/voice/pointerPick.js +++ b/src/voice/pointerPick.js @@ -4,7 +4,7 @@ import { cameraPoseKey } from './pointerContext.js'; import * as defaultAnnotationResolver from '../annotations/annotationResolver.js'; /** "Around here" radius as a share of camera height, clamped to sane bounds. */ -export function pointerRadiusKm(cameraHeightM) { +function pointerRadiusKm(cameraHeightM) { const heightKm = Number(cameraHeightM) / 1000; if (!Number.isFinite(heightKm) || heightKm <= 0) return 50; return Math.round(Math.min(400, Math.max(5, heightKm * 0.75))); diff --git a/src/voice/pointerReticle.js b/src/voice/pointerReticle.js index 96729a933..0702d7345 100644 --- a/src/voice/pointerReticle.js +++ b/src/voice/pointerReticle.js @@ -14,7 +14,7 @@ export const RETICLE_MAX_MS = 10000; export const RETICLE_LINGER_MS = 1500; /** Tools that move the camera away from the pointed spot. */ -export const CAMERA_TOOLS = new Set([ +const CAMERA_TOOLS = new Set([ 'fly_to_location', 'track_entity', 'zoom_to_globe', diff --git a/src/voice/realtimeDiagnostics.js b/src/voice/realtimeDiagnostics.js index 630e29433..55e078280 100644 --- a/src/voice/realtimeDiagnostics.js +++ b/src/voice/realtimeDiagnostics.js @@ -1,10 +1,10 @@ -export const ERROR_LOG_LIMIT = 30; +const ERROR_LOG_LIMIT = 30; -export const ERROR_STORAGE_KEY = 'gev-realtime-errors'; +const ERROR_STORAGE_KEY = 'gev-realtime-errors'; -export const DEBUG_LOG_URL = '/api/realtime/debug-log'; +const DEBUG_LOG_URL = '/api/realtime/debug-log'; -export function createDebugSessionId() { +function createDebugSessionId() { const randomPart = Math.random().toString(36).slice(2, 10); return `gev-${Date.now().toString(36)}-${randomPart}`; } @@ -47,7 +47,7 @@ export function sanitizeDebugValue(value, depth = 0) { return output; } -export function sanitizeDebugString(value) { +function sanitizeDebugString(value) { if (value.startsWith('data:image/')) { return `[Redacted image data URL, ${value.length} chars]`; } @@ -65,13 +65,13 @@ export function sanitizeDebugString(value) { : redacted; } -export function isSecretLikeKey(key) { +function isSecretLikeKey(key) { return /(?:api[_-]?key|authorization|bearer|client[_-]?secret|token|secret|password)/i.test( key, ); } -export function createErrorRecord(source, error, extra = {}) { +function createErrorRecord(source, error, extra = {}) { const rtcError = error?.error || error; return { timestamp: new Date().toISOString(), @@ -90,7 +90,7 @@ export function createErrorRecord(source, error, extra = {}) { }; } -export function formatErrorForDisplay(record) { +function formatErrorForDisplay(record) { const primary = [record.source, record.message].filter(Boolean).join(': '); const state = [ record.errorDetail && `detail=${record.errorDetail}`, @@ -105,7 +105,7 @@ export function formatErrorForDisplay(record) { return state ? `${primary}\n${state}` : primary; } -export function removeEmptyValues(value) { +function removeEmptyValues(value) { return Object.fromEntries( Object.entries(value || {}).filter( ([, item]) => item !== null && item !== undefined && item !== '', @@ -113,14 +113,7 @@ export function removeEmptyValues(value) { ); } -export function compactText(value, maxLength) { - const text = String(value || '') - .replace(/\s+/g, ' ') - .trim(); - return text.length <= maxLength ? text : `${text.slice(0, maxLength - 1)}…`; -} - -export function loadStoredErrors() { +function loadStoredErrors() { try { const value = JSON.parse(localStorage.getItem(ERROR_STORAGE_KEY) || '[]'); return Array.isArray(value) ? value.slice(0, ERROR_LOG_LIMIT) : []; @@ -129,7 +122,7 @@ export function loadStoredErrors() { } } -export function storeErrors(errors) { +function storeErrors(errors) { try { localStorage.setItem( ERROR_STORAGE_KEY, diff --git a/src/voice/realtimePreferences.js b/src/voice/realtimePreferences.js index 1d4d1dd23..d3b78a922 100644 --- a/src/voice/realtimePreferences.js +++ b/src/voice/realtimePreferences.js @@ -8,12 +8,12 @@ import { // Voice cost control (repo-wide `godsEyeView..` convention; // the neighbouring ERROR_STORAGE_KEY predates it). -export const VOICE_TIER_STORAGE_KEY = 'godsEyeView.voiceCost.tier'; +const VOICE_TIER_STORAGE_KEY = 'godsEyeView.voiceCost.tier'; -export const VOICE_LIMITS_STORAGE_KEY = 'godsEyeView.voiceCost.limits'; +const VOICE_LIMITS_STORAGE_KEY = 'godsEyeView.voiceCost.limits'; /** Best-effort localStorage handle; absent in tests and locked-down browsers. */ -export function voiceStorage(storage) { +function voiceStorage(storage) { if (storage) return storage; try { return typeof localStorage === 'undefined' ? null : localStorage; diff --git a/src/voice/realtimeTurns.js b/src/voice/realtimeTurns.js index cbc8d5dbc..2073be06d 100644 --- a/src/voice/realtimeTurns.js +++ b/src/voice/realtimeTurns.js @@ -13,7 +13,7 @@ import { import { narrationLabel } from './speech.js'; /** Out-of-band progress responses carry this metadata tag. */ -export const PROGRESS_RESPONSE_TAG = 'progress'; +const PROGRESS_RESPONSE_TAG = 'progress'; /** * Speaks one code-authored progress line out of band. The line travels as diff --git a/src/voice/realtimeViewport.js b/src/voice/realtimeViewport.js index 755913906..e229b6cc2 100644 --- a/src/voice/realtimeViewport.js +++ b/src/voice/realtimeViewport.js @@ -5,17 +5,17 @@ import { import { cameraPoseKey } from './pointerContext.js'; /** The live viewer's camera pose identity (Cesium-free read). */ -export function readViewerCameraKey() { +function readViewerCameraKey() { return cameraPoseKey(globalThis.window?.__godsEyeView?.viewer); } // Viewport-screenshot size guards (M13). The old code clamped WIDTH only, so a // tall portrait window produced an oversized capture whose dc.send could throw. // Cap total pixels (clamps both dimensions) and drop the image entirely if the // encoded data URL is still too big for the data channel. -export const VIEWPORT_MAX_PIXELS = 1200 * 900; +const VIEWPORT_MAX_PIXELS = 1200 * 900; // ~1.08 MP, matches the old 1200px-wide landscape budget -export const VIEWPORT_MAX_ENCODED_BYTES = 200 * 1024; +const VIEWPORT_MAX_ENCODED_BYTES = 200 * 1024; export async function captureViewportImage() { const viewer = window.__godsEyeView?.viewer; @@ -66,11 +66,11 @@ export async function captureViewportImage() { } /** Side of the point-and-ask crop, in CSS pixels, and its output size. */ -export const POINTER_CROP_SIZE = 512; +const POINTER_CROP_SIZE = 512; /** A pointer snapshot older than this no longer matches the frame. */ -export const POINTER_CROP_MAX_AGE_MS = 20000; +const POINTER_CROP_MAX_AGE_MS = 20000; /** View scales where a crop shows buildings and terrain, not a smear. */ -export const POINTER_CROP_SCALES = new Set(['local', 'city']); +const POINTER_CROP_SCALES = new Set(['local', 'city']); /** * Square source rectangle around a canvas point, clamped inside the canvas, @@ -128,7 +128,7 @@ export function pointerCropRect({ * @returns {Promise<{dataUrl: string, rect: object}|null>} JPEG data URL and * the crop's normalized bounds in the full canvas, or null. */ -export async function capturePointerCrop(screenPx) { +async function capturePointerCrop(screenPx) { const viewer = window.__godsEyeView?.viewer; const source = viewer?.scene?.canvas; if (!source?.width || !source?.height || !screenPx) return null; @@ -259,7 +259,7 @@ export async function renderFreshCesiumFrame(viewer) { } } -export function isNearlyBlackFrame(ctx, width, height) { +function isNearlyBlackFrame(ctx, width, height) { const sampleWidth = Math.min(48, width); const sampleHeight = Math.min(32, height); if (!sampleWidth || !sampleHeight) return true; diff --git a/src/voice/resultDisplay.js b/src/voice/resultDisplay.js index 642db2bb4..ac99f86d7 100644 --- a/src/voice/resultDisplay.js +++ b/src/voice/resultDisplay.js @@ -59,7 +59,7 @@ function notesOf(...values) { ]; } -export function analystNounFor(layerKeys) { +function analystNounFor(layerKeys) { const keys = [...new Set(layerKeys.filter(Boolean))]; if (keys.length !== 1) return 'results'; const key = keys[0]; diff --git a/src/voice/speechLint.js b/src/voice/speechLint.js index 9b6c02ab4..f1b1c1878 100644 --- a/src/voice/speechLint.js +++ b/src/voice/speechLint.js @@ -7,7 +7,7 @@ * Phrases that add length without adding an answer. Tool numbers are spoken * verbatim, so approximations are hedges too. */ -export const HEDGE_PHRASES = Object.freeze([ +const HEDGE_PHRASES = Object.freeze([ 'keep in mind', "it's worth noting", 'it is worth noting', diff --git a/src/voice/turnMetrics.js b/src/voice/turnMetrics.js index 13e9c6940..b2c447cee 100644 --- a/src/voice/turnMetrics.js +++ b/src/voice/turnMetrics.js @@ -1,7 +1,7 @@ import { countWords, findHedges } from './speechLint.js'; /** Quiet period after the last audio or response before a turn is closed. */ -export const TURN_SETTLE_MS = 2500; +const TURN_SETTLE_MS = 2500; /** * Per-turn latency and speech spans for one voice session. diff --git a/src/voice/voiceCard.js b/src/voice/voiceCard.js index 72b754287..09619bb54 100644 --- a/src/voice/voiceCard.js +++ b/src/voice/voiceCard.js @@ -5,7 +5,7 @@ import { } from './voiceCardPresentation.js'; /** Hide an idle card this long after its last update. */ -export const VOICE_CARD_IDLE_MS = 14000; +const VOICE_CARD_IDLE_MS = 14000; const ELEMENT_IDS = Object.freeze({ card: 'gev-voice-card',