@aguacerowx/react-native 0.0.50 → 0.0.52

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.
Files changed (56) hide show
  1. package/android/src/main/java/com/aguacerowx/reactnative/SatelliteLayerView.java +11 -3
  2. package/android/src/main/java/com/aguacerowx/reactnative/WeatherFrameProcessorModule.java +315 -275
  3. package/ios/SatelliteLayerView.swift +11 -4
  4. package/ios/WeatherFrameProcessorModule.swift +222 -188
  5. package/lib/commonjs/WeatherLayerManager.js +112 -48
  6. package/lib/commonjs/WeatherLayerManager.js.map +1 -1
  7. package/lib/commonjs/aguaceroCoreDebugHooks.js +144 -0
  8. package/lib/commonjs/aguaceroCoreDebugHooks.js.map +1 -0
  9. package/lib/commonjs/aguaceroRnDebug.js +358 -0
  10. package/lib/commonjs/aguaceroRnDebug.js.map +1 -0
  11. package/lib/commonjs/gridCdnAuth.js +64 -0
  12. package/lib/commonjs/gridCdnAuth.js.map +1 -0
  13. package/lib/commonjs/index.js +50 -0
  14. package/lib/commonjs/index.js.map +1 -1
  15. package/lib/commonjs/nexrad/nexradAndroidController.js +38 -25
  16. package/lib/commonjs/nexrad/nexradAndroidController.js.map +1 -1
  17. package/lib/commonjs/nexrad/nexradDiag.js +31 -25
  18. package/lib/commonjs/nexrad/nexradDiag.js.map +1 -1
  19. package/lib/commonjs/satellite/satelliteAndroidController.js +24 -15
  20. package/lib/commonjs/satellite/satelliteAndroidController.js.map +1 -1
  21. package/lib/module/WeatherLayerManager.js +112 -48
  22. package/lib/module/WeatherLayerManager.js.map +1 -1
  23. package/lib/module/aguaceroCoreDebugHooks.js +136 -0
  24. package/lib/module/aguaceroCoreDebugHooks.js.map +1 -0
  25. package/lib/module/aguaceroRnDebug.js +341 -0
  26. package/lib/module/aguaceroRnDebug.js.map +1 -0
  27. package/lib/module/gridCdnAuth.js +56 -0
  28. package/lib/module/gridCdnAuth.js.map +1 -0
  29. package/lib/module/index.js +2 -0
  30. package/lib/module/index.js.map +1 -1
  31. package/lib/module/nexrad/nexradAndroidController.js +38 -25
  32. package/lib/module/nexrad/nexradAndroidController.js.map +1 -1
  33. package/lib/module/nexrad/nexradDiag.js +31 -25
  34. package/lib/module/nexrad/nexradDiag.js.map +1 -1
  35. package/lib/module/satellite/satelliteAndroidController.js +24 -15
  36. package/lib/module/satellite/satelliteAndroidController.js.map +1 -1
  37. package/lib/typescript/WeatherLayerManager.d.ts.map +1 -1
  38. package/lib/typescript/aguaceroCoreDebugHooks.d.ts +10 -0
  39. package/lib/typescript/aguaceroCoreDebugHooks.d.ts.map +1 -0
  40. package/lib/typescript/aguaceroRnDebug.d.ts +97 -0
  41. package/lib/typescript/aguaceroRnDebug.d.ts.map +1 -0
  42. package/lib/typescript/gridCdnAuth.d.ts +24 -0
  43. package/lib/typescript/gridCdnAuth.d.ts.map +1 -0
  44. package/lib/typescript/index.d.ts +2 -0
  45. package/lib/typescript/nexrad/nexradAndroidController.d.ts.map +1 -1
  46. package/lib/typescript/nexrad/nexradDiag.d.ts.map +1 -1
  47. package/lib/typescript/satellite/satelliteAndroidController.d.ts.map +1 -1
  48. package/package.json +1 -1
  49. package/src/WeatherLayerManager.js +2024 -1947
  50. package/src/aguaceroCoreDebugHooks.js +142 -0
  51. package/src/aguaceroRnDebug.js +335 -0
  52. package/src/gridCdnAuth.js +56 -0
  53. package/src/index.js +19 -7
  54. package/src/nexrad/nexradAndroidController.js +1078 -1068
  55. package/src/nexrad/nexradDiag.js +150 -144
  56. package/src/satellite/satelliteAndroidController.js +245 -236
@@ -18,6 +18,9 @@ var _satelliteAndroidController = require("./satellite/satelliteAndroidControlle
18
18
  var _NwsAlertsOverlay = _interopRequireDefault(require("./nws/NwsAlertsOverlay"));
19
19
  var _MapRegistry = require("./MapRegistry");
20
20
  var _satelliteBridgeDiag = require("./satelliteBridgeDiag");
21
+ var _aguaceroRnDebug = require("./aguaceroRnDebug");
22
+ var _aguaceroCoreDebugHooks = require("./aguaceroCoreDebugHooks");
23
+ var _gridCdnAuth = require("./gridCdnAuth");
21
24
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
22
25
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
23
26
  // packages/react-native/src/WeatherLayerManager.js
@@ -30,9 +33,9 @@ const SATELLITE_NATIVE = _reactNative.Platform.OS === 'android' || _reactNative.
30
33
  note: 'If you never see sat-bridge logs, the app bundle is not loading this WeatherLayerManager build.'
31
34
  });
32
35
 
33
- /**
34
- * Same filtering as {@link AguaceroCore#_getFilteredMrmsTimestampsForVariable} for older cores
35
- * where that helper (or {@code setMRMSDurationValue}) is missing from the prototype chain.
36
+ /**
37
+ * Same filtering as {@link AguaceroCore#_getFilteredMrmsTimestampsForVariable} for older cores
38
+ * where that helper (or {@code setMRMSDurationValue}) is missing from the prototype chain.
36
39
  */
37
40
  function getFilteredMrmsTimestampsCompat(core, variable) {
38
41
  const raw = core.mrmsStatus?.[variable];
@@ -47,9 +50,9 @@ function getFilteredMrmsTimestampsCompat(core, variable) {
47
50
  return list;
48
51
  }
49
52
 
50
- /**
51
- * Older npm installs may resolve an {@link AguaceroCore} without {@code setMRMSDurationValue} /
52
- * {@code setNexradDurationValue}; mirror those methods here using the same logic as the SDK.
53
+ /**
54
+ * Older npm installs may resolve an {@link AguaceroCore} without {@code setMRMSDurationValue} /
55
+ * {@code setNexradDurationValue}; mirror those methods here using the same logic as the SDK.
53
56
  */
54
57
  async function applyMrmsDurationValue(core, value) {
55
58
  if (typeof core.setMRMSDurationValue === 'function') {
@@ -114,12 +117,12 @@ const {
114
117
  InspectorModule
115
118
  } = _reactNative.NativeModules;
116
119
 
117
- /**
118
- * `state:change` payloads can briefly have {@code isNexrad} before {@code nexradSite} / {@code nexradTimestamp}
119
- * are populated; {@code core.state} is updated first. Merge so readouts match the active radar.
120
- *
121
- * @param {object | null} emitted
122
- * @param {object | null} coreState
120
+ /**
121
+ * `state:change` payloads can briefly have {@code isNexrad} before {@code nexradSite} / {@code nexradTimestamp}
122
+ * are populated; {@code core.state} is updated first. Merge so readouts match the active radar.
123
+ *
124
+ * @param {object | null} emitted
125
+ * @param {object | null} coreState
123
126
  */
124
127
  function mergeNexradEmittedWithCore(emitted, coreState) {
125
128
  if (!emitted || !coreState) return null;
@@ -134,10 +137,10 @@ function mergeNexradEmittedWithCore(emitted, coreState) {
134
137
  };
135
138
  }
136
139
 
137
- /**
138
- * Compact timeline identity for deduping {@code state:change}: extending the NEXRAD/satellite window
139
- * often keeps the same selected unix — without this, {@link WeatherLayerManager} short-circuits and
140
- * never calls native {@code sync} / preload with the expanded frame list.
140
+ /**
141
+ * Compact timeline identity for deduping {@code state:change}: extending the NEXRAD/satellite window
142
+ * often keeps the same selected unix — without this, {@link WeatherLayerManager} short-circuits and
143
+ * never calls native {@code sync} / preload with the expanded frame list.
141
144
  */
142
145
  function nexradObsTimelineSig(state) {
143
146
  const arr = [...(state?.availableNexradTimestamps || [])].map(Number).filter(t => Number.isFinite(t)).sort((a, b) => a - b);
@@ -150,8 +153,9 @@ function satelliteObsTimelineSig(state) {
150
153
  return `${keys.length}:${keys[0]}:${keys[keys.length - 1]}`;
151
154
  }
152
155
 
153
- /** True in __DEV__, or set `globalThis.__AGUACERO_WX_GRID_DEBUG__ = true` in the app entry for release builds. */
156
+ /** True when {@link WeatherLayerManager} `debug` / {@link configureAguaceroRnDebug}, or legacy `__AGUACERO_WX_GRID_DEBUG__`. */
154
157
  function wxGridDebugEnabled() {
158
+ if ((0, _aguaceroRnDebug.isAguaceroRnDebugEnabled)()) return true;
155
159
  try {
156
160
  if (typeof __DEV__ !== 'undefined' && __DEV__) return true;
157
161
  return Boolean(typeof globalThis !== 'undefined' && globalThis.__AGUACERO_WX_GRID_DEBUG__);
@@ -177,37 +181,34 @@ function wxGridWarn(tag, detail) {
177
181
  }
178
182
  }
179
183
 
180
- /**
181
- * Native {@code processFrame} must mirror browser/AguaceroCore grid auth: encoded {@code apiKey}
182
- * in the query string and optional {@code Origin} / {@code Referer} (many CloudFront setups require them).
183
- *
184
- * @param {string} baseGridUrl
185
- * @param {string} resourcePath
186
- * @param {string} apiKey
187
- * @param {string | null | undefined} bundleId
188
- * @param {string | undefined} gridRequestSiteOrigin - e.g. production web origin your edge allowlists (no trailing slash)
184
+ /**
185
+ * Native {@code processFrame} must mirror browser/AguaceroCore grid auth: encoded {@code apiKey}
186
+ * in the query string and optional {@code Origin} / {@code Referer} (many CloudFront setups require them).
187
+ *
188
+ * @param {string} baseGridUrl
189
+ * @param {string} resourcePath
190
+ * @param {string} apiKey
191
+ * @param {string | null | undefined} bundleId
192
+ * @param {string | undefined} gridRequestSiteOrigin - prop or core origin (see {@link resolveGridRequestSiteOrigin})
193
+ * @param {import('@aguacerowx/javascript-sdk').AguaceroCore | null | undefined} core
189
194
  */
190
- function buildGridFrameProcessOptions(baseGridUrl, resourcePath, apiKey, bundleId, gridRequestSiteOrigin) {
191
- const url = `${baseGridUrl}${resourcePath}?apiKey=${encodeURIComponent(apiKey)}`;
195
+ function buildGridFrameProcessOptions(baseGridUrl, resourcePath, apiKey, bundleId, gridRequestSiteOrigin, core) {
196
+ const trimmedKey = typeof apiKey === 'string' ? apiKey.trim() : apiKey;
197
+ const url = `${baseGridUrl}${resourcePath}?apiKey=${encodeURIComponent(trimmedKey || '')}`;
192
198
  const options = {
193
199
  url,
194
- apiKey,
200
+ apiKey: trimmedKey,
195
201
  bundleId
196
202
  };
197
- if (typeof gridRequestSiteOrigin === 'string') {
198
- let origin = gridRequestSiteOrigin.trim();
199
- while (origin.endsWith('/')) {
200
- origin = origin.slice(0, -1);
201
- }
202
- if (origin.length > 0) {
203
- options.gridRequestSiteOrigin = origin;
204
- }
203
+ const origin = (0, _gridCdnAuth.resolveGridRequestSiteOrigin)(gridRequestSiteOrigin, core);
204
+ if (origin) {
205
+ options.gridRequestSiteOrigin = origin;
205
206
  }
206
207
  return options;
207
208
  }
208
209
 
209
- /**
210
- * A helper function to generate the raw RGBA byte buffer for the colormap texture.
210
+ /**
211
+ * A helper function to generate the raw RGBA byte buffer for the colormap texture.
211
212
  */
212
213
  const _generateColormapBytes = colormap => {
213
214
  const width = 256;
@@ -269,8 +270,15 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
269
270
  onNwsAlertClick,
270
271
  /** Same semantics as mapsgl / AguaceroCore: Origin + Referer for grid CDN (required by many CloudFront rules). */
271
272
  gridRequestSiteOrigin,
273
+ /** When true, logs auth/HTTP diagnostics under `[AguaceroRN][debug]` (Metro / Logcat / Xcode). */
274
+ debug = false,
272
275
  ...restProps
273
276
  } = props;
277
+ (0, _react.useEffect)(() => {
278
+ (0, _aguaceroRnDebug.configureAguaceroRnDebug)({
279
+ enabled: Boolean(debug)
280
+ });
281
+ }, [debug]);
274
282
  const context = (0, _react.useContext)(_AguaceroContext.AguaceroContext);
275
283
 
276
284
  // Create the core here instead of getting it from context
@@ -285,6 +293,24 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
285
293
  },
286
294
  autoRefresh: false // <-- add this
287
295
  }), [apiKey, gridRequestSiteOrigin]);
296
+ (0, _react.useEffect)(() => {
297
+ if (!core) return;
298
+ (0, _aguaceroCoreDebugHooks.installAguaceroCoreDebugHooks)(core, {
299
+ gridRequestSiteOriginProp: gridRequestSiteOrigin ?? null,
300
+ debugProp: Boolean(debug)
301
+ });
302
+ if ((0, _aguaceroRnDebug.isAguaceroRnDebugEnabled)() && !apiKey) {
303
+ (0, _aguaceroRnDebug.aguaceroDebugWarn)('WeatherLayerManager.missingApiKey', {
304
+ hint: 'apiKey prop is empty — all CDN requests will fail or return 403'
305
+ });
306
+ }
307
+ if ((0, _aguaceroRnDebug.isAguaceroRnDebugEnabled)() && apiKey && !gridRequestSiteOrigin) {
308
+ (0, _aguaceroRnDebug.aguaceroDebugWarn)('WeatherLayerManager.missingGridOrigin', {
309
+ hint: 'gridRequestSiteOrigin is not set — CloudFront often returns 403 without Origin/Referer on React Native',
310
+ snapshot: (0, _aguaceroRnDebug.getAguaceroAuthDiagnosticSnapshot)(core)
311
+ });
312
+ }
313
+ }, [core, debug, gridRequestSiteOrigin, apiKey]);
288
314
  const [watchesWarningsOptions, setWatchesWarningsOptions] = (0, _react.useState)(() => ({
289
315
  alertInteractionEnabled: true,
290
316
  ...(watchesWarningsProp ?? {})
@@ -388,6 +414,8 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
388
414
  // Track if we've done the initial load
389
415
  const hasInitialLoad = (0, _react.useRef)(false);
390
416
  const hasPreloadedRef = (0, _react.useRef)(false);
417
+ /** Bumped on {@code cancelAllFrames} / full reload so stale {@code processFrame} results are ignored. */
418
+ const preloadGenerationRef = (0, _react.useRef)(0);
391
419
 
392
420
  // Track the last state we processed to avoid redundant updates
393
421
  const lastProcessedState = (0, _react.useRef)(null);
@@ -468,9 +496,9 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
468
496
  refreshData: () => {
469
497
  _checkForUpdates();
470
498
  },
471
- /**
472
- * NWS watches/warnings (native map, iOS + Android): same options as mapsgl {@link WeatherLayerManager#configureWatchesWarnings}.
473
- * @param {object} partial
499
+ /**
500
+ * NWS watches/warnings (native map, iOS + Android): same options as mapsgl {@link WeatherLayerManager#configureWatchesWarnings}.
501
+ * @param {object} partial
474
502
  */
475
503
  configureWatchesWarnings: partial => {
476
504
  setWatchesWarningsOptions(prev => ({
@@ -534,6 +562,8 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
534
562
 
535
563
  // Only mark as "has preloaded" after validation passes
536
564
  hasPreloadedRef.current = true;
565
+ const generation = preloadGenerationRef.current;
566
+ const isStaleGeneration = () => generation !== preloadGenerationRef.current;
537
567
  wxGridVerbose('preloadStart', {
538
568
  isMRMS,
539
569
  model,
@@ -650,7 +680,11 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
650
680
  } else {
651
681
  resourcePath = `/grids/${model}/${date}/${run}/${currentFrame}/${variable}/0`;
652
682
  }
653
- const options = buildGridFrameProcessOptions(core.baseGridUrl, resourcePath, core.apiKey, core.bundleId, gridRequestSiteOrigin);
683
+ const options = (0, _aguaceroRnDebug.augmentProcessFrameOptionsForDebug)(buildGridFrameProcessOptions(core.baseGridUrl, resourcePath, core.apiKey, core.bundleId, gridRequestSiteOrigin, core), core);
684
+ (0, _aguaceroCoreDebugHooks.logProcessFrameAuthMismatch)(core, options, {
685
+ phase: 'preloadCurrent',
686
+ currentCacheKey
687
+ });
654
688
  try {
655
689
  wxGridVerbose('processFrameRequest', {
656
690
  currentCacheKey,
@@ -658,6 +692,13 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
658
692
  frame: currentFrame
659
693
  });
660
694
  const result = await WeatherFrameProcessorModule.processFrame(options);
695
+ if (isStaleGeneration()) {
696
+ wxGridVerbose('preloadCurrentFrameStale', {
697
+ currentCacheKey,
698
+ generation
699
+ });
700
+ return;
701
+ }
661
702
  if (!result || !result.filePath) {
662
703
  hasPreloadedRef.current = false;
663
704
  wxGridWarn('preloadAbort', {
@@ -755,12 +796,20 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
755
796
  const cancelled = e && (e.code === 'E_CANCELLED' || e?.userInfo?.code === 'E_CANCELLED' || typeof e?.message === 'string' && e.message.includes('superseded'));
756
797
  if (!cancelled) {
757
798
  hasPreloadedRef.current = false;
758
- wxGridWarn('preloadProcessFrameError', {
799
+ const errDetail = {
759
800
  currentCacheKey,
760
801
  code: e?.code,
761
802
  message: e?.message,
762
803
  userInfo: e?.userInfo
763
- });
804
+ };
805
+ wxGridWarn('preloadProcessFrameError', errDetail);
806
+ if ((0, _aguaceroRnDebug.isAguaceroRnDebugEnabled)()) {
807
+ (0, _aguaceroRnDebug.aguaceroDebugWarn)('preloadProcessFrameError', {
808
+ ...errDetail,
809
+ auth: (0, _aguaceroRnDebug.getAguaceroAuthDiagnosticSnapshot)(core),
810
+ is403: e?.code === 'HTTP_ERROR' && typeof e?.message === 'string' && e.message.includes('403')
811
+ });
812
+ }
764
813
  } else {
765
814
  wxGridVerbose('preloadProcessFrameCancelled', {
766
815
  currentCacheKey
@@ -785,8 +834,15 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
785
834
  } else {
786
835
  resourcePath = `/grids/${model}/${date}/${run}/${frame}/${variable}/0`;
787
836
  }
788
- const options = buildGridFrameProcessOptions(core.baseGridUrl, resourcePath, core.apiKey, core.bundleId, gridRequestSiteOrigin);
837
+ const options = (0, _aguaceroRnDebug.augmentProcessFrameOptionsForDebug)(buildGridFrameProcessOptions(core.baseGridUrl, resourcePath, core.apiKey, core.bundleId, gridRequestSiteOrigin, core), core);
838
+ (0, _aguaceroCoreDebugHooks.logProcessFrameAuthMismatch)(core, options, {
839
+ phase: 'preloadBackground',
840
+ cacheKey
841
+ });
789
842
  WeatherFrameProcessorModule.processFrame(options).then(result => {
843
+ if (isStaleGeneration()) {
844
+ return;
845
+ }
790
846
  if (!result || !result.filePath) {
791
847
  return;
792
848
  }
@@ -1174,7 +1230,11 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
1174
1230
  m = (frameDate.getUTCMonth() + 1).toString().padStart(2, '0'),
1175
1231
  d = frameDate.getUTCDate().toString().padStart(2, '0');
1176
1232
  const resourcePath = `/grids/mrms/${y}${m}${d}/${frame}/0/${currentVariable}/0`;
1177
- const options = buildGridFrameProcessOptions(core.baseGridUrl, resourcePath, core.apiKey, core.bundleId, gridRequestSiteOrigin);
1233
+ const options = (0, _aguaceroRnDebug.augmentProcessFrameOptionsForDebug)(buildGridFrameProcessOptions(core.baseGridUrl, resourcePath, core.apiKey, core.bundleId, gridRequestSiteOrigin, core), core);
1234
+ (0, _aguaceroCoreDebugHooks.logProcessFrameAuthMismatch)(core, options, {
1235
+ phase: 'mrmsRefresh',
1236
+ cacheKey
1237
+ });
1178
1238
  WeatherFrameProcessorModule.processFrame(options).then(result => {
1179
1239
  if (!result || !result.filePath) return;
1180
1240
  const frameData = {
@@ -1439,6 +1499,7 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
1439
1499
  }
1440
1500
  }
1441
1501
  hasPreloadedRef.current = false;
1502
+ preloadGenerationRef.current += 1;
1442
1503
  preloadedDataCache.current.clear();
1443
1504
  cachedGeometry.current = null;
1444
1505
  cachedColormap.current = null;
@@ -1446,7 +1507,8 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
1446
1507
  WeatherFrameProcessorModule.cancelAllFrames();
1447
1508
  wxGridVerbose('cancelAllFrames', {
1448
1509
  reason: 'needsFullLoad',
1449
- variable: newState.variable
1510
+ variable: newState.variable,
1511
+ generation: preloadGenerationRef.current
1450
1512
  });
1451
1513
  if (!newState.variable) {
1452
1514
  previousStateRef.current = newState;
@@ -1468,6 +1530,8 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
1468
1530
  mrmsTimestamp: newState.mrmsTimestamp,
1469
1531
  forecastHour: newState.forecastHour
1470
1532
  });
1533
+ hasPreloadedRef.current = false;
1534
+ void preloadAllFramesToDisk(newState);
1471
1535
  }
1472
1536
  if (success && newState.opacity !== renderProps.opacity) {
1473
1537
  setRenderProps(prev => ({