@mapvx/web-js 2.1.0-dev.1 → 3.0.0-dev.2

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 (51) hide show
  1. package/dist/cjs/controllers/routeController.js +1 -4
  2. package/dist/cjs/controllers/routeController.js.map +1 -1
  3. package/dist/cjs/index.js.map +1 -1
  4. package/dist/cjs/infrastructure/cache/CacheManager.js.map +1 -1
  5. package/dist/cjs/logger/logger.js +2 -3
  6. package/dist/cjs/logger/logger.js.map +1 -1
  7. package/dist/cjs/logger/sentry.js +1 -1
  8. package/dist/cjs/map/map.js +108 -49
  9. package/dist/cjs/map/map.js.map +1 -1
  10. package/dist/cjs/repository/repository.js +21 -21
  11. package/dist/cjs/repository/repository.js.map +1 -1
  12. package/dist/cjs/sdk.js +21 -21
  13. package/dist/cjs/sdk.js.map +1 -1
  14. package/dist/cjs/utils/opening-hours-helper.js +22 -19
  15. package/dist/cjs/utils/opening-hours-helper.js.map +1 -1
  16. package/dist/cjs/utils/semaphore.js.map +1 -1
  17. package/dist/es/controllers/routeController.d.ts.map +1 -1
  18. package/dist/es/controllers/routeController.js +1 -4
  19. package/dist/es/controllers/routeController.js.map +1 -1
  20. package/dist/es/domain/models/route.d.ts +1 -1
  21. package/dist/es/domain/models/route.d.ts.map +1 -1
  22. package/dist/es/index.d.ts +1 -1
  23. package/dist/es/index.d.ts.map +1 -1
  24. package/dist/es/index.js.map +1 -1
  25. package/dist/es/infrastructure/cache/CacheManager.d.ts.map +1 -1
  26. package/dist/es/infrastructure/cache/CacheManager.js.map +1 -1
  27. package/dist/es/logger/logger.d.ts.map +1 -1
  28. package/dist/es/logger/logger.js +2 -3
  29. package/dist/es/logger/logger.js.map +1 -1
  30. package/dist/es/logger/sentry.js +1 -1
  31. package/dist/es/map/map.d.ts +11 -5
  32. package/dist/es/map/map.d.ts.map +1 -1
  33. package/dist/es/map/map.js +108 -49
  34. package/dist/es/map/map.js.map +1 -1
  35. package/dist/es/repository/repository.js +21 -21
  36. package/dist/es/repository/repository.js.map +1 -1
  37. package/dist/es/sdk.d.ts +26 -1
  38. package/dist/es/sdk.d.ts.map +1 -1
  39. package/dist/es/sdk.js +21 -21
  40. package/dist/es/sdk.js.map +1 -1
  41. package/dist/es/utils/opening-hours-helper.d.ts.map +1 -1
  42. package/dist/es/utils/opening-hours-helper.js +22 -19
  43. package/dist/es/utils/opening-hours-helper.js.map +1 -1
  44. package/dist/es/utils/semaphore.d.ts +1 -1
  45. package/dist/es/utils/semaphore.d.ts.map +1 -1
  46. package/dist/es/utils/semaphore.js.map +1 -1
  47. package/dist/umd/index.js +309 -134
  48. package/dist/umd/index.js.map +1 -1
  49. package/dist/umd/styles.css +0 -12
  50. package/dist/umd/styles.css.map +1 -1
  51. package/package.json +1 -1
@@ -51,6 +51,18 @@ const CIRCLE_LAYER_SUFFIXES = {
51
51
  belowLabels: "-below-labels",
52
52
  top: "-top",
53
53
  };
54
+ /**
55
+ * How many times a failed vector tile is reloaded before it is dropped from the retry
56
+ * queue. Bounded so a permanently unavailable tile stops being requested, while a tile
57
+ * that failed on a transient network error still gets several chances.
58
+ */
59
+ const MAX_TILE_RETRIES = 5;
60
+ /**
61
+ * Base delay for the retry backoff. Attempt N waits `BASE * 2^(N-1)` before it is
62
+ * eligible again — 1s, 2s, 4s, 8s — so a tile that keeps failing is retried ever less
63
+ * often instead of once per timer tick.
64
+ */
65
+ const TILE_RETRY_BASE_DELAY_MS = 1000;
54
66
  /** Fill/line layer ids for one circle render-order bucket. */
55
67
  function circleLayerIdsFor(order) {
56
68
  const suffix = CIRCLE_LAYER_SUFFIXES[order];
@@ -130,7 +142,7 @@ function parsePaddingValue(value, dimension) {
130
142
  if (!match) {
131
143
  return 0;
132
144
  }
133
- const numericValue = parseFloat(match[1]);
145
+ const numericValue = Number.parseFloat(match[1]);
134
146
  const unit = match[2] || "px";
135
147
  switch (unit) {
136
148
  case "px":
@@ -213,6 +225,12 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
213
225
  this.enableHover = false;
214
226
  this.hoveredId = "unselected";
215
227
  this.failedTiles = new Set();
228
+ /**
229
+ * Retry bookkeeping per failed tile. A WeakMap so it can never outlive the tiles
230
+ * themselves — entries disappear with the tile objects, which is why none of the
231
+ * `failedTiles.clear()` paths have to reset it.
232
+ */
233
+ this.tileRetryState = new WeakMap();
216
234
  this.geoLocation = navigator.geolocation;
217
235
  /** Owns the deck.gl overlay + GLB scene; created on style load only when 3D is active. */
218
236
  this.scene3D = undefined;
@@ -331,7 +349,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
331
349
  .catch(console.error);
332
350
  }
333
351
  onMapStyleLoaded(mapConfig, container, style) {
334
- var _a, _b, _c, _d, _e;
352
+ var _a, _b, _c, _d, _e, _f, _g;
335
353
  // Determine if service worker caching should be enabled
336
354
  const useServiceWorkerCaching = this.tileCacheConfig.enabled && this.tileCacheConfig.persistToServiceWorker;
337
355
  if (useServiceWorkerCaching) {
@@ -362,9 +380,9 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
362
380
  (_d = (_c = this.map.touchZoomRotate) === null || _c === void 0 ? void 0 : _c.disableRotation) === null || _d === void 0 ? void 0 : _d.call(_c);
363
381
  }
364
382
  this.map.addControl(new maplibre_gl_1.NavigationControl({
365
- showCompass: mapConfig.showCompass !== undefined ? mapConfig.showCompass : true,
366
- showZoom: mapConfig.showZoom !== undefined ? mapConfig.showZoom : true,
367
- }), (_e = mapConfig.navigationPosition) !== null && _e !== void 0 ? _e : "top-right");
383
+ showCompass: (_e = mapConfig.showCompass) !== null && _e !== void 0 ? _e : true,
384
+ showZoom: (_f = mapConfig.showZoom) !== null && _f !== void 0 ? _f : true,
385
+ }), (_g = mapConfig.navigationPosition) !== null && _g !== void 0 ? _g : "top-right");
368
386
  this.map.on("load", () => {
369
387
  var _a, _b;
370
388
  // 3D libraries load asynchronously; the rest of the load flow does not wait on them.
@@ -465,7 +483,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
465
483
  ]);
466
484
  }
467
485
  catch (error) {
468
- // Layer language update may fail if layer doesn't exist
486
+ this.debugLog("Layer language update failed (layer may not exist)", error);
469
487
  }
470
488
  });
471
489
  // indoor layers
@@ -489,7 +507,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
489
507
  ]);
490
508
  }
491
509
  catch (error) {
492
- // Layer language update may fail if layer doesn't exist
510
+ this.debugLog("Layer language update failed (layer may not exist)", error);
493
511
  }
494
512
  });
495
513
  }
@@ -520,7 +538,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
520
538
  this.logEvent("clearTileCache");
521
539
  }
522
540
  catch (error) {
523
- // Tile cache clear may fail if cache doesn't exist
541
+ this.debugLog("Tile cache clear failed (cache may not exist)", error);
524
542
  }
525
543
  }
526
544
  /**
@@ -609,7 +627,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
609
627
  (_d = options === null || options === void 0 ? void 0 : options.onComplete) === null || _d === void 0 ? void 0 : _d.call(options);
610
628
  }
611
629
  catch (error) {
612
- // Parent place/floor update may fail
630
+ this.debugLog("Parent place/floor update failed", error);
613
631
  }
614
632
  }
615
633
  setLang(lang) {
@@ -677,7 +695,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
677
695
  return markerCreated.id;
678
696
  }
679
697
  catch (error) {
680
- throw new Error("Failed to add marker");
698
+ throw new Error("Failed to add marker", { cause: error });
681
699
  }
682
700
  }
683
701
  updateMarker(markerConfig) {
@@ -691,7 +709,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
691
709
  return (_a = marker === null || marker === void 0 ? void 0 : marker.id) !== null && _a !== void 0 ? _a : "";
692
710
  }
693
711
  catch (error) {
694
- throw new Error("Failed to update marker");
712
+ throw new Error("Failed to update marker", { cause: error });
695
713
  }
696
714
  }
697
715
  updateMarkerPosition(markerId, newPosition) {
@@ -703,7 +721,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
703
721
  this.logEvent("updateMarkerPosition");
704
722
  }
705
723
  catch (error) {
706
- throw new Error("Failed to update marker position");
724
+ throw new Error("Failed to update marker position", { cause: error });
707
725
  }
708
726
  }
709
727
  showMarker(markerId) {
@@ -715,7 +733,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
715
733
  this.logEvent("showMarker");
716
734
  }
717
735
  catch (error) {
718
- // Show marker may fail if marker doesn't exist
736
+ this.debugLog("Show marker failed (marker may not exist)", error);
719
737
  }
720
738
  }
721
739
  hideMarker(markerId) {
@@ -727,7 +745,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
727
745
  this.logEvent("hideMarker");
728
746
  }
729
747
  catch (error) {
730
- // Hide marker may fail if marker doesn't exist
748
+ this.debugLog("Hide marker failed (marker may not exist)", error);
731
749
  }
732
750
  }
733
751
  removeMarker(markerId) {
@@ -740,7 +758,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
740
758
  this.logEvent("removeMarker");
741
759
  }
742
760
  catch (error) {
743
- throw new Error("Failed to remove marker");
761
+ throw new Error("Failed to remove marker", { cause: error });
744
762
  }
745
763
  }
746
764
  removeAllMarkers() {
@@ -752,7 +770,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
752
770
  this.logEvent("removeAllMarkers");
753
771
  }
754
772
  catch (error) {
755
- throw new Error("Failed to remove all markers");
773
+ throw new Error("Failed to remove all markers", { cause: error });
756
774
  }
757
775
  }
758
776
  addCircle(circle) {
@@ -861,7 +879,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
861
879
  this.logEvent("removeCircle");
862
880
  }
863
881
  catch (error) {
864
- throw new Error("Failed to remove circle");
882
+ throw new Error("Failed to remove circle", { cause: error });
865
883
  }
866
884
  }
867
885
  removeAllCircles() {
@@ -871,7 +889,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
871
889
  this.logEvent("removeAllCircles");
872
890
  }
873
891
  catch (error) {
874
- throw new Error("Failed to remove all circles");
892
+ throw new Error("Failed to remove all circles", { cause: error });
875
893
  }
876
894
  }
877
895
  showCircle(circleId) {
@@ -886,6 +904,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
886
904
  return true;
887
905
  }
888
906
  catch (error) {
907
+ this.debugLog("Show circle failed", error);
889
908
  return false;
890
909
  }
891
910
  }
@@ -901,6 +920,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
901
920
  return true;
902
921
  }
903
922
  catch (error) {
923
+ this.debugLog("Hide circle failed", error);
904
924
  return false;
905
925
  }
906
926
  }
@@ -1047,7 +1067,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1047
1067
  }
1048
1068
  }
1049
1069
  catch (error) {
1050
- // Style may not be loaded yet; the styledata listener re-adds the layers
1070
+ this.debugLog("Layers not added: style not loaded yet, the styledata listener will re-add them", error);
1051
1071
  }
1052
1072
  }
1053
1073
  /**
@@ -1066,7 +1086,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1066
1086
  source === null || source === void 0 ? void 0 : source.setData(this.circleFeatureCollection());
1067
1087
  }
1068
1088
  catch (error) {
1069
- // Source may not exist while a new style is loading
1089
+ this.debugLog("Source not found (a new style may still be loading)", error);
1070
1090
  }
1071
1091
  }
1072
1092
  /**
@@ -1096,7 +1116,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1096
1116
  }
1097
1117
  }
1098
1118
  catch (error) {
1099
- // Resize may fail if the map/container is not ready
1119
+ this.debugLog("Resize failed (map/container may not be ready)", error);
1100
1120
  }
1101
1121
  }
1102
1122
  updateMarkersTo(floorId) {
@@ -1257,6 +1277,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1257
1277
  (_a = options === null || options === void 0 ? void 0 : options.onComplete) === null || _a === void 0 ? void 0 : _a.call(options);
1258
1278
  }
1259
1279
  fitCoordinates(coordinates, options) {
1280
+ var _a, _b;
1260
1281
  if (coordinates.length === 0) {
1261
1282
  return;
1262
1283
  }
@@ -1298,8 +1319,8 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1298
1319
  const fitOptions = Object.assign(Object.assign({}, options), { padding });
1299
1320
  const bearing = this.map.getBearing();
1300
1321
  const pitch = this.map.getPitch();
1301
- fitOptions.bearing = fitOptions.bearing !== undefined ? fitOptions.bearing : bearing;
1302
- fitOptions.pitch = fitOptions.pitch !== undefined ? fitOptions.pitch : pitch;
1322
+ (_a = fitOptions.bearing) !== null && _a !== void 0 ? _a : (fitOptions.bearing = bearing);
1323
+ (_b = fitOptions.pitch) !== null && _b !== void 0 ? _b : (fitOptions.pitch = pitch);
1303
1324
  if (onComplete) {
1304
1325
  void this.map.once("moveend", onComplete);
1305
1326
  }
@@ -1307,7 +1328,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1307
1328
  }
1308
1329
  }
1309
1330
  easeToCoordinates(coordinates, options) {
1310
- var _a;
1331
+ var _a, _b, _c;
1311
1332
  if (coordinates.length === 0) {
1312
1333
  return;
1313
1334
  }
@@ -1326,14 +1347,14 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1326
1347
  this.map.easeTo(Object.assign(Object.assign({}, options), { center: { lat: coord.lat, lng: coord.lng }, padding }));
1327
1348
  return;
1328
1349
  }
1329
- const bearing = (options === null || options === void 0 ? void 0 : options.bearing) !== undefined ? options.bearing : this.map.getBearing();
1330
- const pitch = (options === null || options === void 0 ? void 0 : options.pitch) !== undefined ? options.pitch : this.map.getPitch();
1350
+ const bearing = (_a = options === null || options === void 0 ? void 0 : options.bearing) !== null && _a !== void 0 ? _a : this.map.getBearing();
1351
+ const pitch = (_b = options === null || options === void 0 ? void 0 : options.pitch) !== null && _b !== void 0 ? _b : this.map.getPitch();
1331
1352
  const cameraParams = {
1332
1353
  bearing,
1333
1354
  padding,
1334
1355
  containerWidth,
1335
1356
  containerHeight,
1336
- maxZoom: (_a = options === null || options === void 0 ? void 0 : options.maxZoom) !== null && _a !== void 0 ? _a : this.map.getMaxZoom(),
1357
+ maxZoom: (_c = options === null || options === void 0 ? void 0 : options.maxZoom) !== null && _c !== void 0 ? _c : this.map.getMaxZoom(),
1337
1358
  minZoom: this.map.getMinZoom(),
1338
1359
  };
1339
1360
  // Orthographic oriented fit (exact for pitch 0). maplibre's fitBounds only
@@ -1377,7 +1398,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1377
1398
  (_c = options === null || options === void 0 ? void 0 : options.onComplete) === null || _c === void 0 ? void 0 : _c.call(options);
1378
1399
  }
1379
1400
  catch (error) {
1380
- // Floor update may fail
1401
+ this.debugLog("Floor update failed", error);
1381
1402
  }
1382
1403
  }
1383
1404
  updatePlaceAndFloorForRouting(placeId, floorId) {
@@ -1390,7 +1411,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1390
1411
  this.logEvent("updatePlaceAndFloorForRouting", { floorId });
1391
1412
  }
1392
1413
  catch (error) {
1393
- // Place/floor update for routing may fail
1414
+ this.debugLog("Place/floor update for routing failed", error);
1394
1415
  }
1395
1416
  }
1396
1417
  async addRoute(config, drawConfig = {}) {
@@ -1403,7 +1424,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1403
1424
  return route;
1404
1425
  }
1405
1426
  catch (error) {
1406
- throw new Error("Error: Failed to add route");
1427
+ throw new Error("Error: Failed to add route", { cause: error });
1407
1428
  }
1408
1429
  }
1409
1430
  async addLiveRoute(config, drawConfig = {}) {
@@ -1440,8 +1461,8 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1440
1461
  return route;
1441
1462
  }
1442
1463
  catch (error) {
1443
- const cause = error instanceof Error ? error.message : String(error);
1444
- throw new Error(`Error: Failed to add route: ${cause}`);
1464
+ const reason = error instanceof Error ? error.message : String(error);
1465
+ throw new Error(`Error: Failed to add route: ${reason}`, { cause: error });
1445
1466
  }
1446
1467
  }
1447
1468
  updateRouteProgress(routeId, position, behindStyle = { type: "Solid", color: "#276EF1" }) {
@@ -1452,7 +1473,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1452
1473
  this.routeController.updateRouteProgress(routeId, position, behindConfig);
1453
1474
  }
1454
1475
  catch (error) {
1455
- // Update may fail if route doesn't exist
1476
+ this.debugLog("Route update failed (route may not exist)", error);
1456
1477
  }
1457
1478
  }
1458
1479
  removeRoute(routeId) {
@@ -1461,7 +1482,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1461
1482
  this.routeController.removeRoute(routeId);
1462
1483
  }
1463
1484
  catch (error) {
1464
- // Remove route may fail if no route exists
1485
+ this.debugLog("Remove route failed (no route may exist)", error);
1465
1486
  }
1466
1487
  this.syncLiveRouteId();
1467
1488
  }
@@ -1471,7 +1492,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1471
1492
  this.routeController.removeRoute(routeId);
1472
1493
  }
1473
1494
  catch (error) {
1474
- // Remove route may fail if route doesn't exist
1495
+ this.debugLog("Remove route failed (route may not exist)", error);
1475
1496
  }
1476
1497
  this.syncLiveRouteId();
1477
1498
  }
@@ -1481,7 +1502,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1481
1502
  this.routeController.removeAllRoutes();
1482
1503
  }
1483
1504
  catch (error) {
1484
- // Remove all routes may fail
1505
+ this.debugLog("Remove all routes failed", error);
1485
1506
  }
1486
1507
  this.syncLiveRouteId();
1487
1508
  }
@@ -1621,7 +1642,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1621
1642
  }
1622
1643
  }
1623
1644
  catch (error) {
1624
- // Place selection may fail if layer doesn't exist
1645
+ this.debugLog("Place selection failed (layer may not exist)", error);
1625
1646
  }
1626
1647
  }
1627
1648
  is3DStyle() {
@@ -1710,7 +1731,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1710
1731
  return route;
1711
1732
  }
1712
1733
  catch (error) {
1713
- throw new Error("Error: Failed to start animating route");
1734
+ throw new Error("Error: Failed to start animating route", { cause: error });
1714
1735
  }
1715
1736
  }
1716
1737
  async startAnimateRouteV2(routeConfig, drawConfig = {}, animationConfig = {}) {
@@ -1729,7 +1750,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1729
1750
  return route;
1730
1751
  }
1731
1752
  catch (error) {
1732
- throw new Error("Error: Failed to start animating route");
1753
+ throw new Error("Error: Failed to start animating route", { cause: error });
1733
1754
  }
1734
1755
  }
1735
1756
  pauseAnimateRoute(routeId) {
@@ -1738,7 +1759,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1738
1759
  this.logEvent("pauseAnimateRoute");
1739
1760
  }
1740
1761
  catch (error) {
1741
- // Pause animation may fail if no animation is running
1762
+ this.debugLog("Pause animation failed (no animation may be running)", error);
1742
1763
  }
1743
1764
  }
1744
1765
  resumeAnimateRoute(routeId) {
@@ -1747,7 +1768,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1747
1768
  this.logEvent("resumeAnimateRoute");
1748
1769
  }
1749
1770
  catch (error) {
1750
- // Resume animation may fail if no animation is paused
1771
+ this.debugLog("Resume animation failed (no animation may be paused)", error);
1751
1772
  }
1752
1773
  }
1753
1774
  restartAnimateRoute(routeId) {
@@ -1756,7 +1777,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1756
1777
  this.logEvent("restartAnimateRoute");
1757
1778
  }
1758
1779
  catch (error) {
1759
- // Restart animation may fail if no animation exists
1780
+ this.debugLog("Restart animation failed (no animation may exist)", error);
1760
1781
  }
1761
1782
  }
1762
1783
  stopAnimateRoute() {
@@ -1781,7 +1802,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1781
1802
  return route;
1782
1803
  }
1783
1804
  catch (error) {
1784
- throw new Error("Error: Failed to add step animation");
1805
+ throw new Error("Error: Failed to add step animation", { cause: error });
1785
1806
  }
1786
1807
  }
1787
1808
  async addStepAnimationV2(routeConfig, drawConfig = {}, animationConfig = {}) {
@@ -1801,7 +1822,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1801
1822
  return route;
1802
1823
  }
1803
1824
  catch (error) {
1804
- throw new Error("Error: Failed to add step animation");
1825
+ throw new Error("Error: Failed to add step animation", { cause: error });
1805
1826
  }
1806
1827
  }
1807
1828
  nextStepAnimation(routeId) {
@@ -1810,7 +1831,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1810
1831
  this.logEvent("nextStepAnimation");
1811
1832
  }
1812
1833
  catch (error) {
1813
- throw new Error("Error: Failed to animate step");
1834
+ throw new Error("Error: Failed to animate step", { cause: error });
1814
1835
  }
1815
1836
  }
1816
1837
  previousStepAnimation(routeId) {
@@ -1899,7 +1920,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1899
1920
  }
1900
1921
  }
1901
1922
  catch (error) {
1902
- throw new Error("Failed to add border to places");
1923
+ throw new Error("Failed to add border to places", { cause: error });
1903
1924
  }
1904
1925
  }
1905
1926
  createBorderedPlacesLayer() {
@@ -2002,16 +2023,54 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
2002
2023
  // Remove invalid tiles from the set
2003
2024
  tilesToRemove.forEach((tile) => this.failedTiles.delete(tile));
2004
2025
  // Attempt to reload valid failed tiles
2026
+ const now = Date.now();
2005
2027
  tilesToRetry.forEach((tile) => {
2028
+ var _a;
2029
+ const retry = (_a = this.tileRetryState.get(tile)) !== null && _a !== void 0 ? _a : {
2030
+ attempts: 0,
2031
+ nextAttemptAt: 0,
2032
+ pending: false,
2033
+ };
2034
+ // Skip tiles with a reload still in flight or still inside their backoff window.
2035
+ if (retry.pending || now < retry.nextAttemptAt)
2036
+ return;
2037
+ const onFailure = (error) => {
2038
+ const attempts = retry.attempts + 1;
2039
+ if (attempts >= MAX_TILE_RETRIES) {
2040
+ this.debugLog(`Tile reload failed ${attempts} times, dropping it from the retry queue`, error);
2041
+ this.failedTiles.delete(tile);
2042
+ this.tileRetryState.delete(tile);
2043
+ return;
2044
+ }
2045
+ // Stay in the queue and try again once the backoff has elapsed.
2046
+ const backoffMs = TILE_RETRY_BASE_DELAY_MS * 2 ** (attempts - 1);
2047
+ this.debugLog(`Tile reload failed (attempt ${attempts}/${MAX_TILE_RETRIES}), retrying in ${backoffMs}ms`, error);
2048
+ this.tileRetryState.set(tile, {
2049
+ attempts,
2050
+ nextAttemptAt: Date.now() + backoffMs,
2051
+ pending: false,
2052
+ });
2053
+ };
2006
2054
  try {
2007
2055
  // Verify tile still exists and is in a retryable state
2008
2056
  if (tile && (tile.state === "errored" || tile.state === "expired")) {
2009
- source.loadTile(tile);
2057
+ this.tileRetryState.set(tile, Object.assign(Object.assign({}, retry), { pending: true }));
2058
+ // loadTile is async, so a rejection escapes this try/catch and has to be handled
2059
+ // on the promise itself - otherwise it surfaces as an unhandled rejection and the
2060
+ // tile is retried on every tick regardless of how often it has already failed.
2061
+ source
2062
+ .loadTile(tile)
2063
+ .then(() => {
2064
+ // Loaded: forget the attempt history so a later failure starts from zero. The
2065
+ // tile leaves the queue through the tilesToRemove branch on the next tick,
2066
+ // once its state is no longer errored/expired.
2067
+ this.tileRetryState.delete(tile);
2068
+ })
2069
+ .catch(onFailure);
2010
2070
  }
2011
2071
  }
2012
2072
  catch (error) {
2013
- // If reload fails, remove tile from retry queue - this is expected
2014
- this.failedTiles.delete(tile);
2073
+ onFailure(error);
2015
2074
  }
2016
2075
  });
2017
2076
  }, 1000);