@aguacerowx/mapsgl 0.0.54 → 0.0.56

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -1,23 +1,22 @@
1
- // In packages/mapsgl/src/index.js (The final, correct version)
1
+ // In packages/mapsgl/index.js (The final, correct version)
2
2
 
3
3
  // Import the specific classes you want to make public from this package's internal files.
4
4
  import { WeatherLayerManager } from './src/WeatherLayerManager.js';
5
5
  import { MapManager } from './src/MapManager.js';
6
- import { NwsWatchesWarningsOverlay, NWS_DEFAULT_LINE_BEFORE_LAYER_ID } from './src/NwsWatchesWarningsOverlay.js';
7
6
  import {
7
+ NwsWatchesWarningsOverlay,
8
+ NWS_DEFAULT_LINE_BEFORE_LAYER_ID,
8
9
  collectNwsExpressionEventKeys,
9
10
  NWS_ALERT_EVENT_NAMES,
10
11
  NWS_EVENT_COLORS,
11
12
  NWS_SDK_EVENT_COLOR,
12
13
  NWS_SDK_EVENT_NAMES,
13
- } from './src/nwsSdkConstants.js';
14
- import {
15
14
  filterNwsAlertsByIncludedList,
16
15
  filterNwsAlertsByScope,
17
16
  filterToConvectiveSdkEvents,
18
17
  isNwsEventIncludedInAllowlist,
19
18
  SDK_ALLOWED_EVENT_SET,
20
- } from './src/nwsAlertsSupport.js';
19
+ } from '@aguacerowx/javascript-sdk';
21
20
 
22
21
  // Now, export them both so your test app can import them.
23
22
  // This is the "public face" of your @aguacerowx/mapsgl package.
@@ -43,4 +42,4 @@ export {
43
42
  DEFAULT_SATELLITE_WATER_VAPOR_COLORMAP,
44
43
  } from './src/satelliteDefaultColormaps.js';
45
44
 
46
- export { JSDELIVR_BASIS_BASE_URL } from './src/defaultBasisBaseUrl.js';
45
+ export { JSDELIVR_BASIS_BASE_URL } from './src/defaultBasisBaseUrl.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aguacerowx/mapsgl",
3
- "version": "0.0.54",
3
+ "version": "0.0.56",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -10,7 +10,7 @@
10
10
  "scripts": {
11
11
  "prepublishOnly": "npm run bundle-nexrad",
12
12
  "bundle-nexrad": "esbuild src/nexrad/radarDecode.worker.ts --bundle --format=esm --platform=browser --outfile=src/nexrad/radarDecode.worker.bundled.js && esbuild src/nexrad/radarArchiveCore.ts --bundle --format=esm --platform=browser --outfile=src/nexrad/radarArchiveCore.bundled.js --external:@aguacerowx/javascript-sdk && esbuild src/nexrad/MapboxRadarLayer.ts --bundle --format=esm --platform=browser --outfile=src/nexrad/MapboxRadarLayer.bundled.js --external:mapbox-gl --external:@aguacerowx/javascript-sdk && esbuild src/nexrad/nexradCrossSectionSampleAtLatLon.ts --format=esm --platform=browser --outfile=src/nexrad/nexradCrossSectionSampleAtLatLon.bundled.js && esbuild src/nexrad/radarFrameGpuMatch.ts --format=esm --platform=browser --outfile=src/nexrad/radarFrameGpuMatch.bundled.js && esbuild src/nexrad/nexradMapboxFrameOpts.ts --bundle --format=esm --platform=browser --outfile=src/nexrad/nexradMapboxFrameOpts.bundled.js --external:@aguacerowx/javascript-sdk",
13
- "gen:nws-key": "esbuild ../../../aguacero-frontend/src/components/WarningsMenu/nwsWarningCustomizationKey.ts --bundle --format=esm --platform=neutral --outfile=src/nwsWarningCustomizationKey.gen.js"
13
+ "bundle-nexrad-rn": "node scripts/bundle-nexrad-rn.mjs"
14
14
  },
15
15
  "files": [
16
16
  "index.js",
@@ -21,7 +21,7 @@
21
21
  "esbuild": "^0.21.5"
22
22
  },
23
23
  "dependencies": {
24
- "@aguacerowx/javascript-sdk": "^0.0.24",
24
+ "@aguacerowx/javascript-sdk": "^0.0.26",
25
25
  "buffer": "^6.0.3",
26
26
  "fzstd": "^0.1.1",
27
27
  "mapbox-gl": "^3.4.0",
@@ -3,7 +3,13 @@
3
3
  * Preloads all timestamps on the active timeline (MRMS/satellite-style) so scrubbing does not trigger loads.
4
4
  */
5
5
  import { getUnitConversionFunction, getDefaultRadarTilt } from '@aguacerowx/javascript-sdk';
6
- import { fetchAndParseArchive, objectKeyToUrl, setNexradArchiveApiKey } from './nexrad/radarArchiveCore.bundled.js';
6
+ import {
7
+ fetchAndParseArchive,
8
+ objectKeyToUrl,
9
+ setNexradArchiveApiKey,
10
+ setNexradArchiveBundleId,
11
+ setNexradSitesFetchAuth,
12
+ } from './nexrad/radarArchiveCore.bundled.js';
7
13
  import { MapboxRadarLayer } from './nexrad/MapboxRadarLayer.bundled.js';
8
14
  import { nexradBinGroupIdForKey, variableToNexradGroup } from '@aguacerowx/javascript-sdk';
9
15
  import { sampleNexradFrameAtLatLon } from './nexrad/nexradCrossSectionSampleAtLatLon.bundled.js';
@@ -268,6 +274,8 @@ export class NexradWeatherController {
268
274
  this._frameCache.clear();
269
275
 
270
276
  setNexradArchiveApiKey(this.core.apiKey || '');
277
+ setNexradArchiveBundleId(this.core.bundleId || '');
278
+ setNexradSitesFetchAuth(this.core.apiKey || '', this.core.bundleId || '');
271
279
 
272
280
  const times = [...(state.availableNexradTimestamps || [])]
273
281
  .map(Number)
@@ -353,6 +361,8 @@ export class NexradWeatherController {
353
361
  }
354
362
 
355
363
  setNexradArchiveApiKey(this.core.apiKey || '');
364
+ setNexradArchiveBundleId(this.core.bundleId || '');
365
+ setNexradSitesFetchAuth(this.core.apiKey || '', this.core.bundleId || '');
356
366
 
357
367
  const unix = Number(state.nexradTimestamp);
358
368
  const p = this._buildFetchParamsForUnix(state, unix);
@@ -5,8 +5,7 @@ import { GridRenderLayer } from './GridRenderLayer.js'; // <-- IMPORT THE WEB RE
5
5
  import { SatelliteShaderManager } from './SatelliteShaderManager.js';
6
6
  import { NexradWeatherController } from './NexradWeatherController.js';
7
7
  import { NexradSitesOverlay } from './NexradSitesOverlay.js';
8
- import { NwsWatchesWarningsOverlay } from './NwsWatchesWarningsOverlay.js';
9
- import { computeNwsAlertsFetchHoursFromAguaceroState } from './nwsAlertsFetchSpec.js';
8
+ import { NwsWatchesWarningsOverlay, computeNwsAlertsFetchHoursFromAguaceroState } from '@aguacerowx/javascript-sdk';
10
9
  import WorkerPool from './WorkerPool.js';
11
10
 
12
11
  import { DEFAULT_BASIS_BASE_URL } from './defaultBasisBaseUrl.js';
@@ -37,7 +36,7 @@ function findLatestModelRun(modelsData, modelName) {
37
36
  * @param {string} [options.weatherBeforeLayerId] - Same as `belowID`.
38
37
  * @param {string} [options.nexradLayerId] - Override Mapbox id for the NEXRAD custom layer (default: derived from `layerId`).
39
38
  * @param {boolean} [options.mrmsTimelineLog] - When `true`, logs MRMS duration / raw vs filtered timeline counts (prefix `[WeatherLayerManager MRMS]`). Off by default.
40
- * @param {string} [options.basisBaseUrl] - URL prefix for satellite KTX2 Basis transcoder assets (`basis_transcoder.js`, `basis_transcoder.wasm`). Defaults to jsDelivr for the published package version; override (e.g. `/basis/`) for strict CSP or offline.
39
+ * @param {string} [options.gridRequestSiteOrigin] - Forwarded to {@link AguaceroCore}: base URL without trailing slash (e.g. production site or `http://localhost:5173`) so grid `fetch` sends `Origin`/`Referer` outside the browser.
41
40
  */
42
41
  export class WeatherLayerManager extends EventEmitter {
43
42
  constructor(map, options = {}) {
@@ -640,8 +639,7 @@ export class WeatherLayerManager extends EventEmitter {
640
639
  if (!apiKey) {
641
640
  return null;
642
641
  }
643
- const uid = this.core.userId || 'sdk-user';
644
- const url = `https://d3dc62msmxkrd7.cloudfront.net/satellite/${s3FileName}?userId=${encodeURIComponent(uid)}&apiKey=${encodeURIComponent(apiKey)}`;
642
+ const url = `https://d3dc62msmxkrd7.cloudfront.net/satellite/${s3FileName}?userId=${encodeURIComponent('sdk-user')}&apiKey=${encodeURIComponent(apiKey)}`;
645
643
  return { url, shaderFileName, frameKey: Number(satelliteTimestamp), apiKey };
646
644
  }
647
645
 
@@ -1,4 +1,6 @@
1
1
  import type { NexradSite } from './PreprocessedSweepParser.js';
2
+ import nexradSitesDefault from './nexradSitesDefault.json';
3
+ import { nexradArchiveDiag, redactApiKeyFromUrl } from './nexradArchiveDiag.js';
2
4
 
3
5
  type NexradSitesPayload = {
4
6
  sites?: NexradSite[];
@@ -13,22 +15,98 @@ export function setNexradSitesJsonUrl(url: string) {
13
15
  sitesUrl = url || sitesUrl;
14
16
  }
15
17
 
18
+ /**
19
+ * When sitesUrl is HTTPS (e.g. CloudFront), use the same auth as AguaceroCore grid fetches:
20
+ * `?apiKey=`, `x-api-key`, and `x-app-identifier` on React Native when bundle id is set.
21
+ */
22
+ let SITES_FETCH_API_KEY = '';
23
+ let SITES_FETCH_BUNDLE_ID = '';
24
+ export function setNexradSitesFetchAuth(apiKey: string, bundleId?: string) {
25
+ SITES_FETCH_API_KEY = apiKey || '';
26
+ if (bundleId !== undefined) {
27
+ SITES_FETCH_BUNDLE_ID = bundleId || '';
28
+ }
29
+ }
30
+
16
31
  let nexradSitesPayloadPromise: Promise<NexradSitesPayload> | null = null;
17
32
 
33
+ function sitesFetchUrl(): string {
34
+ if (!sitesUrl.startsWith('http') || !SITES_FETCH_API_KEY) {
35
+ return sitesUrl;
36
+ }
37
+ const sep = sitesUrl.includes('?') ? '&' : '?';
38
+ return `${sitesUrl}${sep}apiKey=${SITES_FETCH_API_KEY}`;
39
+ }
40
+
41
+ function sitesFetchHeaders(): Record<string, string> | undefined {
42
+ if (!SITES_FETCH_API_KEY) {
43
+ return undefined;
44
+ }
45
+ const headers: Record<string, string> = {
46
+ 'x-api-key': SITES_FETCH_API_KEY,
47
+ };
48
+ const nav = (globalThis as { navigator?: { product?: string } }).navigator;
49
+ if (nav?.product === 'ReactNative' && SITES_FETCH_BUNDLE_ID) {
50
+ headers['x-app-identifier'] = SITES_FETCH_BUNDLE_ID;
51
+ }
52
+ return headers;
53
+ }
54
+
55
+ function embeddedSitesPayload(): NexradSitesPayload {
56
+ return nexradSitesDefault as NexradSitesPayload;
57
+ }
58
+
18
59
  export function loadNexradSitesPayload(): Promise<NexradSitesPayload> {
19
60
  if (nexradSitesPayloadPromise) {
20
61
  return nexradSitesPayloadPromise;
21
62
  }
22
- nexradSitesPayloadPromise = fetch(sitesUrl)
23
- .then((response) => {
24
- if (!response.ok) throw new Error(`nexrad.json fetch failed: HTTP ${response.status}`);
25
- return response.json() as Promise<NexradSitesPayload>;
26
- })
27
- .catch((error) => {
63
+ nexradSitesPayloadPromise = (async (): Promise<NexradSitesPayload> => {
64
+ const url = sitesFetchUrl();
65
+ const headers = sitesFetchHeaders();
66
+ const headerKeys = headers ? Object.keys(headers).join(',') : '';
67
+ nexradArchiveDiag('sites.fetch.start', {
68
+ canonicalUrl: sitesUrl,
69
+ fetchUrl: redactApiKeyFromUrl(url),
70
+ hasApiKeyQuery: url.includes('apiKey='),
71
+ headerKeys: headerKeys || '(none)',
72
+ apiKeyLen: SITES_FETCH_API_KEY.length,
73
+ bundleIdLen: SITES_FETCH_BUNDLE_ID.length,
74
+ });
75
+ try {
76
+ const response = await fetch(url, headers ? { headers } : undefined);
77
+ if (response.ok) {
78
+ nexradArchiveDiag('sites.fetch.ok', {
79
+ status: response.status,
80
+ canonicalUrl: sitesUrl,
81
+ });
82
+ return (await response.json()) as NexradSitesPayload;
83
+ }
84
+ if (sitesUrl.startsWith('https://')) {
85
+ console.warn(
86
+ `[mapsgl] nexrad.json HTTP ${response.status} for ${sitesUrl} — using embedded site list`,
87
+ );
88
+ nexradArchiveDiag('sites.fetch.fallbackEmbedded', {
89
+ httpStatus: response.status,
90
+ reason: 'HTTPS non-OK — CloudFront may not host /data/nexrad.json; embedded list used',
91
+ canonicalUrl: sitesUrl,
92
+ });
93
+ return embeddedSitesPayload();
94
+ }
95
+ throw new Error(`nexrad.json fetch failed: HTTP ${response.status}`);
96
+ } catch (error) {
97
+ if (sitesUrl.startsWith('https://')) {
98
+ console.warn('[mapsgl] nexrad.json load failed — using embedded site list:', error);
99
+ nexradArchiveDiag('sites.fetch.catchFallbackEmbedded', {
100
+ message: error instanceof Error ? error.message : String(error),
101
+ canonicalUrl: sitesUrl,
102
+ });
103
+ return embeddedSitesPayload();
104
+ }
28
105
  console.error('[mapsgl] Could not load nexrad.json:', error);
29
106
  nexradSitesPayloadPromise = null;
30
107
  throw error;
31
- });
108
+ }
109
+ })();
32
110
  return nexradSitesPayloadPromise;
33
111
  }
34
112
 
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Verbose NEXRAD archive pipeline logs for React Native (Hermes / Logcat).
3
+ * Filter: {@code [Aguacero][NEXRAD][archive]}
4
+ *
5
+ * Omits raw API keys; use lengths / booleans only.
6
+ */
7
+ export type NexradArchiveDiagDetail = Record<string, string | number | boolean | null | undefined>;
8
+
9
+ export function redactApiKeyFromUrl(u: string): string {
10
+ return u.replace(/([?&])apiKey=[^&]*/gi, '$1apiKey=(redacted)');
11
+ }
12
+
13
+ function isReactNative(): boolean {
14
+ const nav = (globalThis as { navigator?: { product?: string } }).navigator;
15
+ return nav?.product === 'ReactNative';
16
+ }
17
+
18
+ /** Logs only on React Native to avoid noisy web consoles (mapsgl path is already observable in DevTools). */
19
+ export function nexradArchiveDiag(phase: string, detail?: NexradArchiveDiagDetail): void {
20
+ if (!isReactNative()) return;
21
+ if (detail !== undefined) {
22
+ console.warn(`[Aguacero][NEXRAD][archive] ${phase}`, detail);
23
+ } else {
24
+ console.warn(`[Aguacero][NEXRAD][archive] ${phase}`);
25
+ }
26
+ }
@@ -221,7 +221,8 @@ function nexradLevel3S3ProductForSiteTilt(siteId, radarVariable, tilt) {
221
221
  return products[idx];
222
222
  }
223
223
  function mapboxFrameUploadOptionsForNexradState(state) {
224
- if (!state || state.nexradDataSource !== "level3") return void 0;
224
+ if (!state) return { geometryLayoutKey: "canonical" };
225
+ if (state.nexradDataSource !== "level3") return { geometryLayoutKey: "canonical" };
225
226
  const site = state.nexradSite;
226
227
  const radarVar = (state.nexradProduct || "REF").toUpperCase();
227
228
  if (nexradLevel3UsesTiltIndexedS3Products(radarVar) && site) {
@@ -235,7 +236,7 @@ function mapboxFrameUploadOptionsForNexradState(state) {
235
236
  const mnemonic = nexradLevel3S3VelocityProductForSiteTilt(site, tilt);
236
237
  return { geometryLayoutKey: `${site}|${radarVar}|${mnemonic}` };
237
238
  }
238
- return { geometryCacheKeysRayBoundaries: true };
239
+ return { geometryLayoutKey: "canonical" };
239
240
  }
240
241
  export {
241
242
  mapboxFrameUploadOptionsForNexradState,
@@ -104,7 +104,8 @@ type NexradStateForMapbox = {
104
104
  export function mapboxFrameUploadOptionsForNexradState(
105
105
  state: NexradStateForMapbox | null | undefined,
106
106
  ): MapboxRadarFrameUploadOptions | undefined {
107
- if (!state || state.nexradDataSource !== 'level3') return undefined;
107
+ if (!state) return { geometryLayoutKey: 'canonical' };
108
+ if (state.nexradDataSource !== 'level3') return { geometryLayoutKey: 'canonical' };
108
109
  const site = state.nexradSite;
109
110
  const radarVar = (state.nexradProduct || 'REF').toUpperCase();
110
111
 
@@ -121,5 +122,5 @@ export function mapboxFrameUploadOptionsForNexradState(
121
122
  return { geometryLayoutKey: `${site}|${radarVar}|${mnemonic}` };
122
123
  }
123
124
 
124
- return { geometryCacheKeysRayBoundaries: true };
125
+ return { geometryLayoutKey: 'canonical' };
125
126
  }