@aguacerowx/react-native 0.0.50 → 0.0.51

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 (47) hide show
  1. package/android/src/main/java/com/aguacerowx/reactnative/WeatherFrameProcessorModule.java +36 -0
  2. package/ios/WeatherFrameProcessorModule.swift +33 -2
  3. package/lib/commonjs/WeatherLayerManager.js +54 -6
  4. package/lib/commonjs/WeatherLayerManager.js.map +1 -1
  5. package/lib/commonjs/aguaceroCoreDebugHooks.js +144 -0
  6. package/lib/commonjs/aguaceroCoreDebugHooks.js.map +1 -0
  7. package/lib/commonjs/aguaceroRnDebug.js +351 -0
  8. package/lib/commonjs/aguaceroRnDebug.js.map +1 -0
  9. package/lib/commonjs/index.js +37 -0
  10. package/lib/commonjs/index.js.map +1 -1
  11. package/lib/commonjs/nexrad/nexradAndroidController.js +13 -0
  12. package/lib/commonjs/nexrad/nexradAndroidController.js.map +1 -1
  13. package/lib/commonjs/nexrad/nexradDiag.js +7 -1
  14. package/lib/commonjs/nexrad/nexradDiag.js.map +1 -1
  15. package/lib/commonjs/satellite/satelliteAndroidController.js +9 -0
  16. package/lib/commonjs/satellite/satelliteAndroidController.js.map +1 -1
  17. package/lib/module/WeatherLayerManager.js +54 -6
  18. package/lib/module/WeatherLayerManager.js.map +1 -1
  19. package/lib/module/aguaceroCoreDebugHooks.js +136 -0
  20. package/lib/module/aguaceroCoreDebugHooks.js.map +1 -0
  21. package/lib/module/aguaceroRnDebug.js +334 -0
  22. package/lib/module/aguaceroRnDebug.js.map +1 -0
  23. package/lib/module/index.js +1 -0
  24. package/lib/module/index.js.map +1 -1
  25. package/lib/module/nexrad/nexradAndroidController.js +13 -0
  26. package/lib/module/nexrad/nexradAndroidController.js.map +1 -1
  27. package/lib/module/nexrad/nexradDiag.js +7 -1
  28. package/lib/module/nexrad/nexradDiag.js.map +1 -1
  29. package/lib/module/satellite/satelliteAndroidController.js +9 -0
  30. package/lib/module/satellite/satelliteAndroidController.js.map +1 -1
  31. package/lib/typescript/WeatherLayerManager.d.ts.map +1 -1
  32. package/lib/typescript/aguaceroCoreDebugHooks.d.ts +10 -0
  33. package/lib/typescript/aguaceroCoreDebugHooks.d.ts.map +1 -0
  34. package/lib/typescript/aguaceroRnDebug.d.ts +97 -0
  35. package/lib/typescript/aguaceroRnDebug.d.ts.map +1 -0
  36. package/lib/typescript/index.d.ts +1 -0
  37. package/lib/typescript/nexrad/nexradAndroidController.d.ts.map +1 -1
  38. package/lib/typescript/nexrad/nexradDiag.d.ts.map +1 -1
  39. package/lib/typescript/satellite/satelliteAndroidController.d.ts.map +1 -1
  40. package/package.json +1 -1
  41. package/src/WeatherLayerManager.js +78 -21
  42. package/src/aguaceroCoreDebugHooks.js +142 -0
  43. package/src/aguaceroRnDebug.js +328 -0
  44. package/src/index.js +8 -0
  45. package/src/nexrad/nexradAndroidController.js +11 -1
  46. package/src/nexrad/nexradDiag.js +7 -1
  47. package/src/satellite/satelliteAndroidController.js +9 -0
@@ -104,6 +104,8 @@ public class WeatherFrameProcessorModule extends ReactContextBaseJavaModule {
104
104
  options.hasKey("gridRequestSiteOrigin") && !options.isNull("gridRequestSiteOrigin")
105
105
  ? options.getString("gridRequestSiteOrigin")
106
106
  : null;
107
+ final boolean debug =
108
+ options.hasKey("debug") && !options.isNull("debug") && options.getBoolean("debug");
107
109
 
108
110
  // FIX 3: Submit to Executor instead of spinning up a raw OS thread
109
111
  taskExecutor.execute(() -> {
@@ -141,6 +143,19 @@ public class WeatherFrameProcessorModule extends ReactContextBaseJavaModule {
141
143
  }
142
144
  Request request = requestBuilder.build();
143
145
 
146
+ if (debug) {
147
+ String redactedUrl = urlString.replaceAll("([?&])apiKey=[^&]*", "$1apiKey=(redacted)");
148
+ Log.w("AguaceroRN", "[debug][FrameProcessor] REQUEST "
149
+ + "method=GET url=" + redactedUrl
150
+ + " apiKey.len=" + (apiKey != null ? apiKey.length() : 0)
151
+ + " bundleId=" + (bundleId != null && !bundleId.isEmpty() ? bundleId : "(none)")
152
+ + " gridOrigin=" + (gridRequestSiteOrigin != null ? gridRequestSiteOrigin : "(none)")
153
+ + " headers=[x-api-key, "
154
+ + (bundleId != null && !bundleId.isEmpty() ? "x-app-identifier, " : "")
155
+ + (gridRequestSiteOrigin != null ? "Origin, Referer" : "no Origin/Referer")
156
+ + "]");
157
+ }
158
+
144
159
  Call call = httpClient.newCall(request);
145
160
  activeCalls.add(call);
146
161
 
@@ -179,6 +194,27 @@ public class WeatherFrameProcessorModule extends ReactContextBaseJavaModule {
179
194
  FileOutputStream fos = null;
180
195
  try {
181
196
  if (!response.isSuccessful()) {
197
+ if (debug) {
198
+ String bodyPeek = "";
199
+ try {
200
+ ResponseBody peekBody = response.peekBody(512);
201
+ if (peekBody != null) {
202
+ bodyPeek = peekBody.string();
203
+ }
204
+ } catch (Exception ignored) {
205
+ bodyPeek = "(unreadable)";
206
+ }
207
+ String redactedUrl = urlString.replaceAll("([?&])apiKey=[^&]*", "$1apiKey=(redacted)");
208
+ Log.e("AguaceroRN", "[debug][FrameProcessor] HTTP_ERROR code=" + response.code()
209
+ + " url=" + redactedUrl
210
+ + " apiKey.len=" + (apiKey != null ? apiKey.length() : 0)
211
+ + " bundleId=" + (bundleId != null && !bundleId.isEmpty() ? bundleId : "(none)")
212
+ + " gridOrigin=" + (gridRequestSiteOrigin != null ? gridRequestSiteOrigin : "(none)")
213
+ + " body=" + bodyPeek);
214
+ if (response.code() == 403) {
215
+ Log.e("AguaceroRN", "[debug][FrameProcessor] 403 hint: verify API key, allowlisted bundleId (x-app-identifier), and gridRequestSiteOrigin (Origin/Referer) match your web app.");
216
+ }
217
+ }
182
218
  promise.reject("HTTP_ERROR", "HTTP " + response.code());
183
219
  return;
184
220
  }
@@ -46,13 +46,15 @@ class WeatherFrameProcessorModule: NSObject {
46
46
 
47
47
  let apiKey = options["apiKey"] as? String ?? ""
48
48
  let bundleId = options["bundleId"] as? String
49
+ let debug = (options["debug"] as? Bool) == true
50
+ let gridOriginOpt = options["gridRequestSiteOrigin"] as? String
49
51
 
50
52
  var request = URLRequest(url: url)
51
53
  request.setValue(apiKey, forHTTPHeaderField: "x-api-key")
52
54
  if let bundleId = bundleId, !bundleId.isEmpty {
53
55
  request.setValue(bundleId, forHTTPHeaderField: "x-app-identifier")
54
56
  }
55
- if let site = options["gridRequestSiteOrigin"] as? String {
57
+ if let site = gridOriginOpt {
56
58
  var origin = site.trimmingCharacters(in: .whitespacesAndNewlines)
57
59
  while origin.hasSuffix("/") {
58
60
  origin.removeLast()
@@ -63,6 +65,17 @@ class WeatherFrameProcessorModule: NSObject {
63
65
  }
64
66
  }
65
67
 
68
+ if debug {
69
+ let redacted = urlString.replacingOccurrences(
70
+ of: #"([?&])apiKey=[^&]*"#,
71
+ with: "$1apiKey=(redacted)",
72
+ options: .regularExpression
73
+ )
74
+ let bundleStr = (bundleId?.isEmpty == false) ? bundleId! : "(none)"
75
+ let originStr = (gridOriginOpt?.isEmpty == false) ? gridOriginOpt! : "(none)"
76
+ print("[AguaceroRN][debug][FrameProcessor] REQUEST url=\(redacted) apiKey.len=\(apiKey.count) bundleId=\(bundleStr) gridOrigin=\(originStr)")
77
+ }
78
+
66
79
  let task = session.dataTask(with: request) { data, response, error in
67
80
  let fetchDuration = (CFAbsoluteTimeGetCurrent() - startTime) * 1000
68
81
 
@@ -89,7 +102,25 @@ class WeatherFrameProcessorModule: NSObject {
89
102
 
90
103
  guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else {
91
104
  let statusCode = (response as? HTTPURLResponse)?.statusCode ?? -1
92
- print("🔍 [WeatherFrameProcessor] \(fileId) HTTP_ERROR status=\(statusCode)")
105
+ if debug {
106
+ let redacted = urlString.replacingOccurrences(
107
+ of: #"([?&])apiKey=[^&]*"#,
108
+ with: "$1apiKey=(redacted)",
109
+ options: .regularExpression
110
+ )
111
+ var bodySnippet = ""
112
+ if let data = data, let text = String(data: data, encoding: .utf8) {
113
+ bodySnippet = text.count > 512 ? String(text.prefix(512)) + "…" : text
114
+ }
115
+ let bundleStr = (bundleId?.isEmpty == false) ? bundleId! : "(none)"
116
+ let originStr = (gridOriginOpt?.isEmpty == false) ? gridOriginOpt! : "(none)"
117
+ print("[AguaceroRN][debug][FrameProcessor] HTTP_ERROR status=\(statusCode) url=\(redacted) apiKey.len=\(apiKey.count) bundleId=\(bundleStr) gridOrigin=\(originStr) body=\(bodySnippet)")
118
+ if statusCode == 403 {
119
+ print("[AguaceroRN][debug][FrameProcessor] 403 hint: verify API key, allowlisted bundleId (x-app-identifier), and gridRequestSiteOrigin (Origin/Referer) match your web app.")
120
+ }
121
+ } else {
122
+ print("🔍 [WeatherFrameProcessor] \(fileId) HTTP_ERROR status=\(statusCode)")
123
+ }
93
124
  reject("HTTP_ERROR", "HTTP request failed with status code: \(statusCode)", nil)
94
125
  return
95
126
  }
@@ -18,6 +18,8 @@ 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");
21
23
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
22
24
  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
25
  // packages/react-native/src/WeatherLayerManager.js
@@ -150,8 +152,9 @@ function satelliteObsTimelineSig(state) {
150
152
  return `${keys.length}:${keys[0]}:${keys[keys.length - 1]}`;
151
153
  }
152
154
 
153
- /** True in __DEV__, or set `globalThis.__AGUACERO_WX_GRID_DEBUG__ = true` in the app entry for release builds. */
155
+ /** True when {@link WeatherLayerManager} `debug` / {@link configureAguaceroRnDebug}, or legacy `__AGUACERO_WX_GRID_DEBUG__`. */
154
156
  function wxGridDebugEnabled() {
157
+ if ((0, _aguaceroRnDebug.isAguaceroRnDebugEnabled)()) return true;
155
158
  try {
156
159
  if (typeof __DEV__ !== 'undefined' && __DEV__) return true;
157
160
  return Boolean(typeof globalThis !== 'undefined' && globalThis.__AGUACERO_WX_GRID_DEBUG__);
@@ -269,8 +272,15 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
269
272
  onNwsAlertClick,
270
273
  /** Same semantics as mapsgl / AguaceroCore: Origin + Referer for grid CDN (required by many CloudFront rules). */
271
274
  gridRequestSiteOrigin,
275
+ /** When true, logs auth/HTTP diagnostics under `[AguaceroRN][debug]` (Metro / Logcat / Xcode). */
276
+ debug = false,
272
277
  ...restProps
273
278
  } = props;
279
+ (0, _react.useEffect)(() => {
280
+ (0, _aguaceroRnDebug.configureAguaceroRnDebug)({
281
+ enabled: Boolean(debug)
282
+ });
283
+ }, [debug]);
274
284
  const context = (0, _react.useContext)(_AguaceroContext.AguaceroContext);
275
285
 
276
286
  // Create the core here instead of getting it from context
@@ -285,6 +295,24 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
285
295
  },
286
296
  autoRefresh: false // <-- add this
287
297
  }), [apiKey, gridRequestSiteOrigin]);
298
+ (0, _react.useEffect)(() => {
299
+ if (!core) return;
300
+ (0, _aguaceroCoreDebugHooks.installAguaceroCoreDebugHooks)(core, {
301
+ gridRequestSiteOriginProp: gridRequestSiteOrigin ?? null,
302
+ debugProp: Boolean(debug)
303
+ });
304
+ if ((0, _aguaceroRnDebug.isAguaceroRnDebugEnabled)() && !apiKey) {
305
+ (0, _aguaceroRnDebug.aguaceroDebugWarn)('WeatherLayerManager.missingApiKey', {
306
+ hint: 'apiKey prop is empty — all CDN requests will fail or return 403'
307
+ });
308
+ }
309
+ if ((0, _aguaceroRnDebug.isAguaceroRnDebugEnabled)() && apiKey && !gridRequestSiteOrigin) {
310
+ (0, _aguaceroRnDebug.aguaceroDebugWarn)('WeatherLayerManager.missingGridOrigin', {
311
+ hint: 'gridRequestSiteOrigin is not set — CloudFront often returns 403 without Origin/Referer on React Native',
312
+ snapshot: (0, _aguaceroRnDebug.getAguaceroAuthDiagnosticSnapshot)(core)
313
+ });
314
+ }
315
+ }, [core, debug, gridRequestSiteOrigin, apiKey]);
288
316
  const [watchesWarningsOptions, setWatchesWarningsOptions] = (0, _react.useState)(() => ({
289
317
  alertInteractionEnabled: true,
290
318
  ...(watchesWarningsProp ?? {})
@@ -650,7 +678,11 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
650
678
  } else {
651
679
  resourcePath = `/grids/${model}/${date}/${run}/${currentFrame}/${variable}/0`;
652
680
  }
653
- const options = buildGridFrameProcessOptions(core.baseGridUrl, resourcePath, core.apiKey, core.bundleId, gridRequestSiteOrigin);
681
+ const options = (0, _aguaceroRnDebug.augmentProcessFrameOptionsForDebug)(buildGridFrameProcessOptions(core.baseGridUrl, resourcePath, core.apiKey, core.bundleId, gridRequestSiteOrigin), core);
682
+ (0, _aguaceroCoreDebugHooks.logProcessFrameAuthMismatch)(core, options, {
683
+ phase: 'preloadCurrent',
684
+ currentCacheKey
685
+ });
654
686
  try {
655
687
  wxGridVerbose('processFrameRequest', {
656
688
  currentCacheKey,
@@ -755,12 +787,20 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
755
787
  const cancelled = e && (e.code === 'E_CANCELLED' || e?.userInfo?.code === 'E_CANCELLED' || typeof e?.message === 'string' && e.message.includes('superseded'));
756
788
  if (!cancelled) {
757
789
  hasPreloadedRef.current = false;
758
- wxGridWarn('preloadProcessFrameError', {
790
+ const errDetail = {
759
791
  currentCacheKey,
760
792
  code: e?.code,
761
793
  message: e?.message,
762
794
  userInfo: e?.userInfo
763
- });
795
+ };
796
+ wxGridWarn('preloadProcessFrameError', errDetail);
797
+ if ((0, _aguaceroRnDebug.isAguaceroRnDebugEnabled)()) {
798
+ (0, _aguaceroRnDebug.aguaceroDebugWarn)('preloadProcessFrameError', {
799
+ ...errDetail,
800
+ auth: (0, _aguaceroRnDebug.getAguaceroAuthDiagnosticSnapshot)(core),
801
+ is403: e?.code === 'HTTP_ERROR' && typeof e?.message === 'string' && e.message.includes('403')
802
+ });
803
+ }
764
804
  } else {
765
805
  wxGridVerbose('preloadProcessFrameCancelled', {
766
806
  currentCacheKey
@@ -785,7 +825,11 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
785
825
  } else {
786
826
  resourcePath = `/grids/${model}/${date}/${run}/${frame}/${variable}/0`;
787
827
  }
788
- const options = buildGridFrameProcessOptions(core.baseGridUrl, resourcePath, core.apiKey, core.bundleId, gridRequestSiteOrigin);
828
+ const options = (0, _aguaceroRnDebug.augmentProcessFrameOptionsForDebug)(buildGridFrameProcessOptions(core.baseGridUrl, resourcePath, core.apiKey, core.bundleId, gridRequestSiteOrigin), core);
829
+ (0, _aguaceroCoreDebugHooks.logProcessFrameAuthMismatch)(core, options, {
830
+ phase: 'preloadBackground',
831
+ cacheKey
832
+ });
789
833
  WeatherFrameProcessorModule.processFrame(options).then(result => {
790
834
  if (!result || !result.filePath) {
791
835
  return;
@@ -1174,7 +1218,11 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
1174
1218
  m = (frameDate.getUTCMonth() + 1).toString().padStart(2, '0'),
1175
1219
  d = frameDate.getUTCDate().toString().padStart(2, '0');
1176
1220
  const resourcePath = `/grids/mrms/${y}${m}${d}/${frame}/0/${currentVariable}/0`;
1177
- const options = buildGridFrameProcessOptions(core.baseGridUrl, resourcePath, core.apiKey, core.bundleId, gridRequestSiteOrigin);
1221
+ const options = (0, _aguaceroRnDebug.augmentProcessFrameOptionsForDebug)(buildGridFrameProcessOptions(core.baseGridUrl, resourcePath, core.apiKey, core.bundleId, gridRequestSiteOrigin), core);
1222
+ (0, _aguaceroCoreDebugHooks.logProcessFrameAuthMismatch)(core, options, {
1223
+ phase: 'mrmsRefresh',
1224
+ cacheKey
1225
+ });
1178
1226
  WeatherFrameProcessorModule.processFrame(options).then(result => {
1179
1227
  if (!result || !result.filePath) return;
1180
1228
  const frameData = {