@mapvx/web-js 2.0.2 → 3.0.0-dev.1

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 (66) hide show
  1. package/dist/cjs/controllers/routeController.js +26 -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 +123 -49
  9. package/dist/cjs/map/map.js.map +1 -1
  10. package/dist/cjs/map/scene3DController.js +93 -8
  11. package/dist/cjs/map/scene3DController.js.map +1 -1
  12. package/dist/cjs/repository/repository.js +21 -21
  13. package/dist/cjs/repository/repository.js.map +1 -1
  14. package/dist/cjs/sdk.js +21 -21
  15. package/dist/cjs/sdk.js.map +1 -1
  16. package/dist/cjs/utils/3d.js +34 -0
  17. package/dist/cjs/utils/3d.js.map +1 -1
  18. package/dist/cjs/utils/opening-hours-helper.js +22 -19
  19. package/dist/cjs/utils/opening-hours-helper.js.map +1 -1
  20. package/dist/cjs/utils/semaphore.js.map +1 -1
  21. package/dist/es/controllers/routeController.d.ts +8 -0
  22. package/dist/es/controllers/routeController.d.ts.map +1 -1
  23. package/dist/es/controllers/routeController.js +26 -4
  24. package/dist/es/controllers/routeController.js.map +1 -1
  25. package/dist/es/domain/models/route.d.ts +1 -1
  26. package/dist/es/domain/models/route.d.ts.map +1 -1
  27. package/dist/es/domain/models/scene3D.d.ts +34 -0
  28. package/dist/es/domain/models/scene3D.d.ts.map +1 -1
  29. package/dist/es/index.d.ts +1 -1
  30. package/dist/es/index.d.ts.map +1 -1
  31. package/dist/es/index.js.map +1 -1
  32. package/dist/es/infrastructure/cache/CacheManager.d.ts.map +1 -1
  33. package/dist/es/infrastructure/cache/CacheManager.js.map +1 -1
  34. package/dist/es/logger/logger.d.ts.map +1 -1
  35. package/dist/es/logger/logger.js +2 -3
  36. package/dist/es/logger/logger.js.map +1 -1
  37. package/dist/es/logger/sentry.js +1 -1
  38. package/dist/es/map/map.d.ts +24 -5
  39. package/dist/es/map/map.d.ts.map +1 -1
  40. package/dist/es/map/map.js +123 -49
  41. package/dist/es/map/map.js.map +1 -1
  42. package/dist/es/map/scene3DController.d.ts +31 -4
  43. package/dist/es/map/scene3DController.d.ts.map +1 -1
  44. package/dist/es/map/scene3DController.js +94 -9
  45. package/dist/es/map/scene3DController.js.map +1 -1
  46. package/dist/es/repository/repository.js +21 -21
  47. package/dist/es/repository/repository.js.map +1 -1
  48. package/dist/es/sdk.d.ts +26 -1
  49. package/dist/es/sdk.d.ts.map +1 -1
  50. package/dist/es/sdk.js +21 -21
  51. package/dist/es/sdk.js.map +1 -1
  52. package/dist/es/utils/3d.d.ts +32 -1
  53. package/dist/es/utils/3d.d.ts.map +1 -1
  54. package/dist/es/utils/3d.js +31 -0
  55. package/dist/es/utils/3d.js.map +1 -1
  56. package/dist/es/utils/opening-hours-helper.d.ts.map +1 -1
  57. package/dist/es/utils/opening-hours-helper.js +22 -19
  58. package/dist/es/utils/opening-hours-helper.js.map +1 -1
  59. package/dist/es/utils/semaphore.d.ts +1 -1
  60. package/dist/es/utils/semaphore.d.ts.map +1 -1
  61. package/dist/es/utils/semaphore.js.map +1 -1
  62. package/dist/umd/index.js +598 -199
  63. package/dist/umd/index.js.map +1 -1
  64. package/dist/umd/styles.css +0 -12
  65. package/dist/umd/styles.css.map +1 -1
  66. 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
  /**
@@ -537,6 +555,21 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
537
555
  var _a;
538
556
  return (_a = this.currentFloor) !== null && _a !== void 0 ? _a : "";
539
557
  }
558
+ /**
559
+ * Sets the route path used for GLB route-transparency collision, so 3D models a route passes
560
+ * through fade and reveal the floor plan underneath. Called by {@link RouteController} whenever
561
+ * the set of drawn routes changes. Points are tagged with the floor their segment is drawn on;
562
+ * only points on the displayed floor participate. Pass an empty array to clear.
563
+ *
564
+ * No-op unless the 3D scene is active.
565
+ */
566
+ setRouteCollisionPath(points) {
567
+ var _a;
568
+ if (!this.is3DSceneActive()) {
569
+ return;
570
+ }
571
+ (_a = this.scene3D) === null || _a === void 0 ? void 0 : _a.setRouteCollisionPath(points);
572
+ }
540
573
  /**
541
574
  * Performs changes when parent place is updated
542
575
  * @param place new parent place
@@ -594,7 +627,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
594
627
  (_d = options === null || options === void 0 ? void 0 : options.onComplete) === null || _d === void 0 ? void 0 : _d.call(options);
595
628
  }
596
629
  catch (error) {
597
- // Parent place/floor update may fail
630
+ this.debugLog("Parent place/floor update failed", error);
598
631
  }
599
632
  }
600
633
  setLang(lang) {
@@ -662,7 +695,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
662
695
  return markerCreated.id;
663
696
  }
664
697
  catch (error) {
665
- throw new Error("Failed to add marker");
698
+ throw new Error("Failed to add marker", { cause: error });
666
699
  }
667
700
  }
668
701
  updateMarker(markerConfig) {
@@ -676,7 +709,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
676
709
  return (_a = marker === null || marker === void 0 ? void 0 : marker.id) !== null && _a !== void 0 ? _a : "";
677
710
  }
678
711
  catch (error) {
679
- throw new Error("Failed to update marker");
712
+ throw new Error("Failed to update marker", { cause: error });
680
713
  }
681
714
  }
682
715
  updateMarkerPosition(markerId, newPosition) {
@@ -688,7 +721,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
688
721
  this.logEvent("updateMarkerPosition");
689
722
  }
690
723
  catch (error) {
691
- throw new Error("Failed to update marker position");
724
+ throw new Error("Failed to update marker position", { cause: error });
692
725
  }
693
726
  }
694
727
  showMarker(markerId) {
@@ -700,7 +733,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
700
733
  this.logEvent("showMarker");
701
734
  }
702
735
  catch (error) {
703
- // Show marker may fail if marker doesn't exist
736
+ this.debugLog("Show marker failed (marker may not exist)", error);
704
737
  }
705
738
  }
706
739
  hideMarker(markerId) {
@@ -712,7 +745,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
712
745
  this.logEvent("hideMarker");
713
746
  }
714
747
  catch (error) {
715
- // Hide marker may fail if marker doesn't exist
748
+ this.debugLog("Hide marker failed (marker may not exist)", error);
716
749
  }
717
750
  }
718
751
  removeMarker(markerId) {
@@ -725,7 +758,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
725
758
  this.logEvent("removeMarker");
726
759
  }
727
760
  catch (error) {
728
- throw new Error("Failed to remove marker");
761
+ throw new Error("Failed to remove marker", { cause: error });
729
762
  }
730
763
  }
731
764
  removeAllMarkers() {
@@ -737,7 +770,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
737
770
  this.logEvent("removeAllMarkers");
738
771
  }
739
772
  catch (error) {
740
- throw new Error("Failed to remove all markers");
773
+ throw new Error("Failed to remove all markers", { cause: error });
741
774
  }
742
775
  }
743
776
  addCircle(circle) {
@@ -846,7 +879,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
846
879
  this.logEvent("removeCircle");
847
880
  }
848
881
  catch (error) {
849
- throw new Error("Failed to remove circle");
882
+ throw new Error("Failed to remove circle", { cause: error });
850
883
  }
851
884
  }
852
885
  removeAllCircles() {
@@ -856,7 +889,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
856
889
  this.logEvent("removeAllCircles");
857
890
  }
858
891
  catch (error) {
859
- throw new Error("Failed to remove all circles");
892
+ throw new Error("Failed to remove all circles", { cause: error });
860
893
  }
861
894
  }
862
895
  showCircle(circleId) {
@@ -871,6 +904,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
871
904
  return true;
872
905
  }
873
906
  catch (error) {
907
+ this.debugLog("Show circle failed", error);
874
908
  return false;
875
909
  }
876
910
  }
@@ -886,6 +920,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
886
920
  return true;
887
921
  }
888
922
  catch (error) {
923
+ this.debugLog("Hide circle failed", error);
889
924
  return false;
890
925
  }
891
926
  }
@@ -1032,7 +1067,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1032
1067
  }
1033
1068
  }
1034
1069
  catch (error) {
1035
- // 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);
1036
1071
  }
1037
1072
  }
1038
1073
  /**
@@ -1051,7 +1086,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1051
1086
  source === null || source === void 0 ? void 0 : source.setData(this.circleFeatureCollection());
1052
1087
  }
1053
1088
  catch (error) {
1054
- // Source may not exist while a new style is loading
1089
+ this.debugLog("Source not found (a new style may still be loading)", error);
1055
1090
  }
1056
1091
  }
1057
1092
  /**
@@ -1081,7 +1116,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1081
1116
  }
1082
1117
  }
1083
1118
  catch (error) {
1084
- // Resize may fail if the map/container is not ready
1119
+ this.debugLog("Resize failed (map/container may not be ready)", error);
1085
1120
  }
1086
1121
  }
1087
1122
  updateMarkersTo(floorId) {
@@ -1242,6 +1277,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1242
1277
  (_a = options === null || options === void 0 ? void 0 : options.onComplete) === null || _a === void 0 ? void 0 : _a.call(options);
1243
1278
  }
1244
1279
  fitCoordinates(coordinates, options) {
1280
+ var _a, _b;
1245
1281
  if (coordinates.length === 0) {
1246
1282
  return;
1247
1283
  }
@@ -1283,8 +1319,8 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1283
1319
  const fitOptions = Object.assign(Object.assign({}, options), { padding });
1284
1320
  const bearing = this.map.getBearing();
1285
1321
  const pitch = this.map.getPitch();
1286
- fitOptions.bearing = fitOptions.bearing !== undefined ? fitOptions.bearing : bearing;
1287
- 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);
1288
1324
  if (onComplete) {
1289
1325
  void this.map.once("moveend", onComplete);
1290
1326
  }
@@ -1292,7 +1328,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1292
1328
  }
1293
1329
  }
1294
1330
  easeToCoordinates(coordinates, options) {
1295
- var _a;
1331
+ var _a, _b, _c;
1296
1332
  if (coordinates.length === 0) {
1297
1333
  return;
1298
1334
  }
@@ -1311,14 +1347,14 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1311
1347
  this.map.easeTo(Object.assign(Object.assign({}, options), { center: { lat: coord.lat, lng: coord.lng }, padding }));
1312
1348
  return;
1313
1349
  }
1314
- const bearing = (options === null || options === void 0 ? void 0 : options.bearing) !== undefined ? options.bearing : this.map.getBearing();
1315
- 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();
1316
1352
  const cameraParams = {
1317
1353
  bearing,
1318
1354
  padding,
1319
1355
  containerWidth,
1320
1356
  containerHeight,
1321
- 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(),
1322
1358
  minZoom: this.map.getMinZoom(),
1323
1359
  };
1324
1360
  // Orthographic oriented fit (exact for pitch 0). maplibre's fitBounds only
@@ -1362,7 +1398,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1362
1398
  (_c = options === null || options === void 0 ? void 0 : options.onComplete) === null || _c === void 0 ? void 0 : _c.call(options);
1363
1399
  }
1364
1400
  catch (error) {
1365
- // Floor update may fail
1401
+ this.debugLog("Floor update failed", error);
1366
1402
  }
1367
1403
  }
1368
1404
  updatePlaceAndFloorForRouting(placeId, floorId) {
@@ -1375,7 +1411,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1375
1411
  this.logEvent("updatePlaceAndFloorForRouting", { floorId });
1376
1412
  }
1377
1413
  catch (error) {
1378
- // Place/floor update for routing may fail
1414
+ this.debugLog("Place/floor update for routing failed", error);
1379
1415
  }
1380
1416
  }
1381
1417
  async addRoute(config, drawConfig = {}) {
@@ -1388,7 +1424,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1388
1424
  return route;
1389
1425
  }
1390
1426
  catch (error) {
1391
- throw new Error("Error: Failed to add route");
1427
+ throw new Error("Error: Failed to add route", { cause: error });
1392
1428
  }
1393
1429
  }
1394
1430
  async addLiveRoute(config, drawConfig = {}) {
@@ -1425,8 +1461,8 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1425
1461
  return route;
1426
1462
  }
1427
1463
  catch (error) {
1428
- const cause = error instanceof Error ? error.message : String(error);
1429
- 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 });
1430
1466
  }
1431
1467
  }
1432
1468
  updateRouteProgress(routeId, position, behindStyle = { type: "Solid", color: "#276EF1" }) {
@@ -1437,7 +1473,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1437
1473
  this.routeController.updateRouteProgress(routeId, position, behindConfig);
1438
1474
  }
1439
1475
  catch (error) {
1440
- // Update may fail if route doesn't exist
1476
+ this.debugLog("Route update failed (route may not exist)", error);
1441
1477
  }
1442
1478
  }
1443
1479
  removeRoute(routeId) {
@@ -1446,7 +1482,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1446
1482
  this.routeController.removeRoute(routeId);
1447
1483
  }
1448
1484
  catch (error) {
1449
- // Remove route may fail if no route exists
1485
+ this.debugLog("Remove route failed (no route may exist)", error);
1450
1486
  }
1451
1487
  this.syncLiveRouteId();
1452
1488
  }
@@ -1456,7 +1492,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1456
1492
  this.routeController.removeRoute(routeId);
1457
1493
  }
1458
1494
  catch (error) {
1459
- // Remove route may fail if route doesn't exist
1495
+ this.debugLog("Remove route failed (route may not exist)", error);
1460
1496
  }
1461
1497
  this.syncLiveRouteId();
1462
1498
  }
@@ -1466,7 +1502,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1466
1502
  this.routeController.removeAllRoutes();
1467
1503
  }
1468
1504
  catch (error) {
1469
- // Remove all routes may fail
1505
+ this.debugLog("Remove all routes failed", error);
1470
1506
  }
1471
1507
  this.syncLiveRouteId();
1472
1508
  }
@@ -1606,7 +1642,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1606
1642
  }
1607
1643
  }
1608
1644
  catch (error) {
1609
- // Place selection may fail if layer doesn't exist
1645
+ this.debugLog("Place selection failed (layer may not exist)", error);
1610
1646
  }
1611
1647
  }
1612
1648
  is3DStyle() {
@@ -1695,7 +1731,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1695
1731
  return route;
1696
1732
  }
1697
1733
  catch (error) {
1698
- throw new Error("Error: Failed to start animating route");
1734
+ throw new Error("Error: Failed to start animating route", { cause: error });
1699
1735
  }
1700
1736
  }
1701
1737
  async startAnimateRouteV2(routeConfig, drawConfig = {}, animationConfig = {}) {
@@ -1714,7 +1750,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1714
1750
  return route;
1715
1751
  }
1716
1752
  catch (error) {
1717
- throw new Error("Error: Failed to start animating route");
1753
+ throw new Error("Error: Failed to start animating route", { cause: error });
1718
1754
  }
1719
1755
  }
1720
1756
  pauseAnimateRoute(routeId) {
@@ -1723,7 +1759,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1723
1759
  this.logEvent("pauseAnimateRoute");
1724
1760
  }
1725
1761
  catch (error) {
1726
- // Pause animation may fail if no animation is running
1762
+ this.debugLog("Pause animation failed (no animation may be running)", error);
1727
1763
  }
1728
1764
  }
1729
1765
  resumeAnimateRoute(routeId) {
@@ -1732,7 +1768,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1732
1768
  this.logEvent("resumeAnimateRoute");
1733
1769
  }
1734
1770
  catch (error) {
1735
- // Resume animation may fail if no animation is paused
1771
+ this.debugLog("Resume animation failed (no animation may be paused)", error);
1736
1772
  }
1737
1773
  }
1738
1774
  restartAnimateRoute(routeId) {
@@ -1741,7 +1777,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1741
1777
  this.logEvent("restartAnimateRoute");
1742
1778
  }
1743
1779
  catch (error) {
1744
- // Restart animation may fail if no animation exists
1780
+ this.debugLog("Restart animation failed (no animation may exist)", error);
1745
1781
  }
1746
1782
  }
1747
1783
  stopAnimateRoute() {
@@ -1766,7 +1802,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1766
1802
  return route;
1767
1803
  }
1768
1804
  catch (error) {
1769
- throw new Error("Error: Failed to add step animation");
1805
+ throw new Error("Error: Failed to add step animation", { cause: error });
1770
1806
  }
1771
1807
  }
1772
1808
  async addStepAnimationV2(routeConfig, drawConfig = {}, animationConfig = {}) {
@@ -1786,7 +1822,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1786
1822
  return route;
1787
1823
  }
1788
1824
  catch (error) {
1789
- throw new Error("Error: Failed to add step animation");
1825
+ throw new Error("Error: Failed to add step animation", { cause: error });
1790
1826
  }
1791
1827
  }
1792
1828
  nextStepAnimation(routeId) {
@@ -1795,7 +1831,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1795
1831
  this.logEvent("nextStepAnimation");
1796
1832
  }
1797
1833
  catch (error) {
1798
- throw new Error("Error: Failed to animate step");
1834
+ throw new Error("Error: Failed to animate step", { cause: error });
1799
1835
  }
1800
1836
  }
1801
1837
  previousStepAnimation(routeId) {
@@ -1884,7 +1920,7 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1884
1920
  }
1885
1921
  }
1886
1922
  catch (error) {
1887
- throw new Error("Failed to add border to places");
1923
+ throw new Error("Failed to add border to places", { cause: error });
1888
1924
  }
1889
1925
  }
1890
1926
  createBorderedPlacesLayer() {
@@ -1987,16 +2023,54 @@ class InternalMapVXMap extends loggeable_1.Loggeable {
1987
2023
  // Remove invalid tiles from the set
1988
2024
  tilesToRemove.forEach((tile) => this.failedTiles.delete(tile));
1989
2025
  // Attempt to reload valid failed tiles
2026
+ const now = Date.now();
1990
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
+ };
1991
2054
  try {
1992
2055
  // Verify tile still exists and is in a retryable state
1993
2056
  if (tile && (tile.state === "errored" || tile.state === "expired")) {
1994
- 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);
1995
2070
  }
1996
2071
  }
1997
2072
  catch (error) {
1998
- // If reload fails, remove tile from retry queue - this is expected
1999
- this.failedTiles.delete(tile);
2073
+ onFailure(error);
2000
2074
  }
2001
2075
  });
2002
2076
  }, 1000);