@pol-studios/powersync 1.0.6 → 1.0.7

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 (118) hide show
  1. package/dist/CacheSettingsManager-1exbOC6S.d.ts +261 -0
  2. package/dist/attachments/index.d.ts +65 -355
  3. package/dist/attachments/index.js +24 -6
  4. package/dist/{types-Cd7RhNqf.d.ts → background-sync-ChCXW-EV.d.ts} +53 -2
  5. package/dist/chunk-4C3RY5SU.js +204 -0
  6. package/dist/chunk-4C3RY5SU.js.map +1 -0
  7. package/dist/{chunk-3AYXHQ4W.js → chunk-53WH2JJV.js} +111 -47
  8. package/dist/chunk-53WH2JJV.js.map +1 -0
  9. package/dist/chunk-A4IBBWGO.js +377 -0
  10. package/dist/chunk-A4IBBWGO.js.map +1 -0
  11. package/dist/chunk-BREGB4WL.js +1768 -0
  12. package/dist/chunk-BREGB4WL.js.map +1 -0
  13. package/dist/{chunk-EJ23MXPQ.js → chunk-CGL33PL4.js} +3 -1
  14. package/dist/chunk-CGL33PL4.js.map +1 -0
  15. package/dist/chunk-DGUM43GV.js +11 -0
  16. package/dist/chunk-DHYUBVP7.js +131 -0
  17. package/dist/chunk-DHYUBVP7.js.map +1 -0
  18. package/dist/chunk-FV2HXEIY.js +124 -0
  19. package/dist/chunk-FV2HXEIY.js.map +1 -0
  20. package/dist/chunk-GKF7TOMT.js +1 -0
  21. package/dist/{chunk-R4YFWQ3Q.js → chunk-H772V6XQ.js} +304 -51
  22. package/dist/chunk-H772V6XQ.js.map +1 -0
  23. package/dist/{chunk-62J2DPKX.js → chunk-HFOFLW5F.js} +396 -412
  24. package/dist/chunk-HFOFLW5F.js.map +1 -0
  25. package/dist/chunk-KGSFAE5B.js +1 -0
  26. package/dist/chunk-LNL64IJZ.js +1 -0
  27. package/dist/chunk-MKD2VCX3.js +32 -0
  28. package/dist/chunk-MKD2VCX3.js.map +1 -0
  29. package/dist/{chunk-7EMDVIZX.js → chunk-N75DEF5J.js} +19 -1
  30. package/dist/chunk-N75DEF5J.js.map +1 -0
  31. package/dist/chunk-P6WOZO7H.js +49 -0
  32. package/dist/chunk-P6WOZO7H.js.map +1 -0
  33. package/dist/chunk-TGBT5XBE.js +1 -0
  34. package/dist/chunk-TGBT5XBE.js.map +1 -0
  35. package/dist/chunk-UEYRTLKE.js +72 -0
  36. package/dist/chunk-UEYRTLKE.js.map +1 -0
  37. package/dist/chunk-WGHNIAF7.js +329 -0
  38. package/dist/chunk-WGHNIAF7.js.map +1 -0
  39. package/dist/chunk-WQ5MPAVC.js +449 -0
  40. package/dist/chunk-WQ5MPAVC.js.map +1 -0
  41. package/dist/{chunk-FPTDATY5.js → chunk-XQAJM2MW.js} +22 -11
  42. package/dist/chunk-XQAJM2MW.js.map +1 -0
  43. package/dist/chunk-YSTEESEG.js +676 -0
  44. package/dist/chunk-YSTEESEG.js.map +1 -0
  45. package/dist/chunk-ZEOKPWUC.js +1165 -0
  46. package/dist/chunk-ZEOKPWUC.js.map +1 -0
  47. package/dist/connector/index.d.ts +182 -3
  48. package/dist/connector/index.js +12 -4
  49. package/dist/core/index.d.ts +5 -3
  50. package/dist/core/index.js +5 -2
  51. package/dist/error/index.d.ts +54 -0
  52. package/dist/error/index.js +8 -0
  53. package/dist/error/index.js.map +1 -0
  54. package/dist/index.d.ts +100 -12
  55. package/dist/index.js +148 -38
  56. package/dist/index.native.d.ts +20 -10
  57. package/dist/index.native.js +148 -39
  58. package/dist/index.web.d.ts +20 -10
  59. package/dist/index.web.js +149 -39
  60. package/dist/maintenance/index.d.ts +118 -0
  61. package/dist/maintenance/index.js +17 -0
  62. package/dist/maintenance/index.js.map +1 -0
  63. package/dist/platform/index.d.ts +16 -1
  64. package/dist/platform/index.js +2 -0
  65. package/dist/platform/index.js.map +1 -1
  66. package/dist/platform/index.native.d.ts +2 -2
  67. package/dist/platform/index.native.js +2 -1
  68. package/dist/platform/index.web.d.ts +1 -1
  69. package/dist/platform/index.web.js +2 -1
  70. package/dist/pol-attachment-queue-C7YNXXhK.d.ts +676 -0
  71. package/dist/provider/index.d.ts +447 -21
  72. package/dist/provider/index.js +33 -13
  73. package/dist/storage/index.d.ts +6 -0
  74. package/dist/storage/index.js +28 -0
  75. package/dist/storage/index.js.map +1 -0
  76. package/dist/storage/index.native.d.ts +6 -0
  77. package/dist/storage/index.native.js +26 -0
  78. package/dist/storage/index.native.js.map +1 -0
  79. package/dist/storage/index.web.d.ts +6 -0
  80. package/dist/storage/index.web.js +26 -0
  81. package/dist/storage/index.web.js.map +1 -0
  82. package/dist/storage/upload/index.d.ts +55 -0
  83. package/dist/storage/upload/index.js +15 -0
  84. package/dist/storage/upload/index.js.map +1 -0
  85. package/dist/storage/upload/index.native.d.ts +57 -0
  86. package/dist/storage/upload/index.native.js +14 -0
  87. package/dist/storage/upload/index.native.js.map +1 -0
  88. package/dist/storage/upload/index.web.d.ts +5 -0
  89. package/dist/storage/upload/index.web.js +14 -0
  90. package/dist/storage/upload/index.web.js.map +1 -0
  91. package/dist/{index-l3iL9Jte.d.ts → supabase-connector-qLm-WHkM.d.ts} +90 -25
  92. package/dist/sync/index.d.ts +288 -23
  93. package/dist/sync/index.js +22 -10
  94. package/dist/types-BVacP54t.d.ts +52 -0
  95. package/dist/types-Bgvx7-E8.d.ts +187 -0
  96. package/dist/{types-afHtE1U_.d.ts → types-CDqWh56B.d.ts} +2 -0
  97. package/package.json +72 -2
  98. package/dist/chunk-32OLICZO.js +0 -1
  99. package/dist/chunk-3AYXHQ4W.js.map +0 -1
  100. package/dist/chunk-5FIMA26D.js +0 -1
  101. package/dist/chunk-62J2DPKX.js.map +0 -1
  102. package/dist/chunk-7EMDVIZX.js.map +0 -1
  103. package/dist/chunk-EJ23MXPQ.js.map +0 -1
  104. package/dist/chunk-FPTDATY5.js.map +0 -1
  105. package/dist/chunk-KCDG2MNP.js +0 -1431
  106. package/dist/chunk-KCDG2MNP.js.map +0 -1
  107. package/dist/chunk-OLHGI472.js +0 -1
  108. package/dist/chunk-PAFBKNL3.js +0 -99
  109. package/dist/chunk-PAFBKNL3.js.map +0 -1
  110. package/dist/chunk-R4YFWQ3Q.js.map +0 -1
  111. package/dist/chunk-V6LJ6MR2.js +0 -740
  112. package/dist/chunk-V6LJ6MR2.js.map +0 -1
  113. package/dist/chunk-VJCL2SWD.js +0 -1
  114. package/dist/failed-upload-store-C0cLxxPz.d.ts +0 -33
  115. /package/dist/{chunk-32OLICZO.js.map → chunk-DGUM43GV.js.map} +0 -0
  116. /package/dist/{chunk-5FIMA26D.js.map → chunk-GKF7TOMT.js.map} +0 -0
  117. /package/dist/{chunk-OLHGI472.js.map → chunk-KGSFAE5B.js.map} +0 -0
  118. /package/dist/{chunk-VJCL2SWD.js.map → chunk-LNL64IJZ.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/retry.ts"],"sourcesContent":["/**\n * Exponential backoff retry utilities for resilient network operations.\n *\n * Provides configurable retry logic with exponential backoff, jitter,\n * and abort signal support for cancellation.\n */\n\n/**\n * Configuration for exponential backoff retry behavior.\n */\nexport interface BackoffConfig {\n /** Maximum number of retry attempts (0 = no retries, just one attempt) */\n maxRetries: number;\n /** Base delay in milliseconds before first retry */\n baseDelayMs: number;\n /** Maximum delay cap in milliseconds */\n maxDelayMs: number;\n /** Multiplier applied to delay for each subsequent retry (typically 2) */\n backoffMultiplier: number;\n}\n\n/**\n * Options for the withExponentialBackoff function.\n */\nexport interface BackoffOptions {\n /** Optional AbortSignal for cancellation support */\n signal?: AbortSignal;\n /** Callback invoked before each retry attempt */\n onRetry?: (attempt: number, delay: number, error: Error) => void;\n}\n\n/**\n * Error thrown when an operation is aborted via AbortSignal.\n */\nexport class AbortError extends Error {\n constructor(message = 'Operation aborted') {\n super(message);\n this.name = 'AbortError';\n }\n}\n\n/**\n * Error thrown when all retry attempts have been exhausted.\n */\nexport class RetryExhaustedError extends Error {\n /** The last error that caused the final retry to fail */\n readonly cause: Error;\n /** Total number of attempts made */\n readonly attempts: number;\n constructor(cause: Error, attempts: number) {\n super(`Retry exhausted after ${attempts} attempt(s): ${cause.message}`);\n this.name = 'RetryExhaustedError';\n this.cause = cause;\n this.attempts = attempts;\n }\n}\n\n/**\n * Calculates the delay for a given retry attempt using exponential backoff.\n *\n * Formula: min(baseDelay * (multiplier ^ attempt), maxDelay)\n *\n * @param attempt - The current attempt number (0-indexed)\n * @param config - Backoff configuration\n * @returns Delay in milliseconds (without jitter)\n *\n * @example\n * ```ts\n * const config = { baseDelayMs: 1000, maxDelayMs: 30000, backoffMultiplier: 2 };\n * calculateBackoffDelay(0, config); // 1000\n * calculateBackoffDelay(1, config); // 2000\n * calculateBackoffDelay(2, config); // 4000\n * calculateBackoffDelay(5, config); // 30000 (capped at maxDelayMs)\n * ```\n */\nexport function calculateBackoffDelay(attempt: number, config: Pick<BackoffConfig, 'baseDelayMs' | 'maxDelayMs' | 'backoffMultiplier'>): number {\n const {\n baseDelayMs,\n maxDelayMs,\n backoffMultiplier\n } = config;\n\n // Ensure non-negative attempt number\n const safeAttempt = Math.max(0, attempt);\n\n // Calculate exponential delay\n const exponentialDelay = baseDelayMs * Math.pow(backoffMultiplier, safeAttempt);\n\n // Cap at maxDelayMs\n return Math.min(exponentialDelay, maxDelayMs);\n}\n\n/**\n * Adds jitter (±10%) to a delay value to prevent thundering herd.\n *\n * @param delay - Base delay in milliseconds\n * @returns Delay with random jitter applied\n */\nexport function addJitter(delay: number): number {\n // Generate random factor between 0.9 and 1.1 (±10%)\n const jitterFactor = 0.9 + Math.random() * 0.2;\n return Math.round(delay * jitterFactor);\n}\n\n/**\n * Sleep utility that respects AbortSignal for cancellation.\n *\n * @param ms - Duration to sleep in milliseconds\n * @param signal - Optional AbortSignal for cancellation\n * @returns Promise that resolves after the delay or rejects if aborted\n * @throws {AbortError} If the signal is aborted during sleep\n *\n * @example\n * ```ts\n * const controller = new AbortController();\n *\n * // Sleep for 1 second\n * await sleep(1000);\n *\n * // Sleep with cancellation support\n * await sleep(1000, controller.signal);\n *\n * // Cancel the sleep\n * controller.abort();\n * ```\n */\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n // Check if already aborted\n if (signal?.aborted) {\n reject(new AbortError());\n return;\n }\n\n // Handle zero or negative duration\n if (ms <= 0) {\n resolve();\n return;\n }\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n const handleAbort = () => {\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n reject(new AbortError());\n };\n\n // Set up abort listener if signal provided\n if (signal) {\n signal.addEventListener('abort', handleAbort, {\n once: true\n });\n }\n timeoutId = setTimeout(() => {\n // Clean up abort listener\n if (signal) {\n signal.removeEventListener('abort', handleAbort);\n }\n resolve();\n }, ms);\n });\n}\n\n/**\n * Default backoff configuration suitable for most network operations.\n */\nexport const DEFAULT_BACKOFF_CONFIG: BackoffConfig = {\n maxRetries: 3,\n baseDelayMs: 1000,\n maxDelayMs: 30000,\n backoffMultiplier: 2\n};\n\n/**\n * Executes a function with exponential backoff retry logic.\n *\n * Retries the provided function on failure using exponential backoff with jitter.\n * Supports cancellation via AbortSignal and provides hooks for retry observation.\n *\n * @param fn - Async function to execute with retry logic\n * @param config - Backoff configuration (maxRetries, delays, multiplier)\n * @param options - Optional abort signal and retry callback\n * @returns Promise resolving to the function result\n * @throws {AbortError} If operation is aborted via signal\n * @throws {RetryExhaustedError} If all retry attempts fail\n *\n * @example\n * ```ts\n * const controller = new AbortController();\n *\n * const result = await withExponentialBackoff(\n * async () => {\n * const response = await fetch('https://api.example.com/data');\n * if (!response.ok) throw new Error('Request failed');\n * return response.json();\n * },\n * {\n * maxRetries: 3,\n * baseDelayMs: 1000,\n * maxDelayMs: 30000,\n * backoffMultiplier: 2,\n * },\n * {\n * signal: controller.signal,\n * onRetry: (attempt, delay, error) => {\n * console.log(`Retry ${attempt} in ${delay}ms: ${error.message}`);\n * },\n * }\n * );\n * ```\n */\nexport async function withExponentialBackoff<T>(fn: () => Promise<T>, config: BackoffConfig, options?: BackoffOptions): Promise<T> {\n const {\n maxRetries,\n baseDelayMs,\n maxDelayMs,\n backoffMultiplier\n } = config;\n const {\n signal,\n onRetry\n } = options ?? {};\n\n // Check for immediate abort\n if (signal?.aborted) {\n throw new AbortError();\n }\n\n // Validate config\n const safeMaxRetries = Math.max(0, Math.floor(maxRetries));\n const totalAttempts = safeMaxRetries + 1; // Initial attempt + retries\n\n let lastError: Error | undefined;\n for (let attempt = 0; attempt < totalAttempts; attempt++) {\n // Check for abort before each attempt\n if (signal?.aborted) {\n throw new AbortError();\n }\n try {\n return await fn();\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n\n // Check if this was the last attempt\n const isLastAttempt = attempt === totalAttempts - 1;\n if (isLastAttempt) {\n break;\n }\n\n // Check for abort before sleeping\n if (signal?.aborted) {\n throw new AbortError();\n }\n\n // Calculate delay with jitter\n const baseDelay = calculateBackoffDelay(attempt, {\n baseDelayMs,\n maxDelayMs,\n backoffMultiplier\n });\n const delayWithJitter = addJitter(baseDelay);\n\n // Notify about upcoming retry\n onRetry?.(attempt + 1, delayWithJitter, lastError);\n\n // Wait before retry\n await sleep(delayWithJitter, signal);\n }\n }\n\n // All attempts exhausted\n throw new RetryExhaustedError(lastError!, totalAttempts);\n}"],"mappings":";AAkCO,IAAM,aAAN,cAAyB,MAAM;AAAA,EACpC,YAAY,UAAU,qBAAqB;AACzC,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAKO,IAAM,sBAAN,cAAkC,MAAM;AAAA;AAAA,EAEpC;AAAA;AAAA,EAEA;AAAA,EACT,YAAY,OAAc,UAAkB;AAC1C,UAAM,yBAAyB,QAAQ,gBAAgB,MAAM,OAAO,EAAE;AACtE,SAAK,OAAO;AACZ,SAAK,QAAQ;AACb,SAAK,WAAW;AAAA,EAClB;AACF;AAoBO,SAAS,sBAAsB,SAAiB,QAAyF;AAC9I,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAGJ,QAAM,cAAc,KAAK,IAAI,GAAG,OAAO;AAGvC,QAAM,mBAAmB,cAAc,KAAK,IAAI,mBAAmB,WAAW;AAG9E,SAAO,KAAK,IAAI,kBAAkB,UAAU;AAC9C;AAQO,SAAS,UAAU,OAAuB;AAE/C,QAAM,eAAe,MAAM,KAAK,OAAO,IAAI;AAC3C,SAAO,KAAK,MAAM,QAAQ,YAAY;AACxC;AAwBO,SAAS,MAAM,IAAY,QAAqC;AACrE,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AAEtC,QAAI,QAAQ,SAAS;AACnB,aAAO,IAAI,WAAW,CAAC;AACvB;AAAA,IACF;AAGA,QAAI,MAAM,GAAG;AACX,cAAQ;AACR;AAAA,IACF;AACA,QAAI;AACJ,UAAM,cAAc,MAAM;AACxB,UAAI,cAAc,QAAW;AAC3B,qBAAa,SAAS;AAAA,MACxB;AACA,aAAO,IAAI,WAAW,CAAC;AAAA,IACzB;AAGA,QAAI,QAAQ;AACV,aAAO,iBAAiB,SAAS,aAAa;AAAA,QAC5C,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AACA,gBAAY,WAAW,MAAM;AAE3B,UAAI,QAAQ;AACV,eAAO,oBAAoB,SAAS,WAAW;AAAA,MACjD;AACA,cAAQ;AAAA,IACV,GAAG,EAAE;AAAA,EACP,CAAC;AACH;AAKO,IAAM,yBAAwC;AAAA,EACnD,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,mBAAmB;AACrB;AAwCA,eAAsB,uBAA0B,IAAsB,QAAuB,SAAsC;AACjI,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AACJ,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,EACF,IAAI,WAAW,CAAC;AAGhB,MAAI,QAAQ,SAAS;AACnB,UAAM,IAAI,WAAW;AAAA,EACvB;AAGA,QAAM,iBAAiB,KAAK,IAAI,GAAG,KAAK,MAAM,UAAU,CAAC;AACzD,QAAM,gBAAgB,iBAAiB;AAEvC,MAAI;AACJ,WAAS,UAAU,GAAG,UAAU,eAAe,WAAW;AAExD,QAAI,QAAQ,SAAS;AACnB,YAAM,IAAI,WAAW;AAAA,IACvB;AACA,QAAI;AACF,aAAO,MAAM,GAAG;AAAA,IAClB,SAAS,OAAO;AACd,kBAAY,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAGpE,YAAM,gBAAgB,YAAY,gBAAgB;AAClD,UAAI,eAAe;AACjB;AAAA,MACF;AAGA,UAAI,QAAQ,SAAS;AACnB,cAAM,IAAI,WAAW;AAAA,MACvB;AAGA,YAAM,YAAY,sBAAsB,SAAS;AAAA,QAC/C;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AACD,YAAM,kBAAkB,UAAU,SAAS;AAG3C,gBAAU,UAAU,GAAG,iBAAiB,SAAS;AAGjD,YAAM,MAAM,iBAAiB,MAAM;AAAA,IACrC;AAAA,EACF;AAGA,QAAM,IAAI,oBAAoB,WAAY,aAAa;AACzD;","names":[]}
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=chunk-GKF7TOMT.js.map
@@ -4,14 +4,15 @@ import {
4
4
  LATENCY_DEGRADED_THRESHOLD_MS,
5
5
  MAX_CONSECUTIVE_FAILURES,
6
6
  STATUS_NOTIFY_THROTTLE_MS,
7
+ STORAGE_KEY_AUTO_OFFLINE,
7
8
  STORAGE_KEY_METRICS,
8
9
  STORAGE_KEY_PAUSED,
9
10
  STORAGE_KEY_SYNC_MODE
10
- } from "./chunk-EJ23MXPQ.js";
11
+ } from "./chunk-CGL33PL4.js";
11
12
  import {
12
13
  classifyError,
13
14
  generateFailureId
14
- } from "./chunk-FPTDATY5.js";
15
+ } from "./chunk-XQAJM2MW.js";
15
16
 
16
17
  // src/provider/types.ts
17
18
  var DEFAULT_SYNC_STATUS = {
@@ -51,6 +52,8 @@ var DEFAULT_SYNC_CONFIG = {
51
52
  };
52
53
 
53
54
  // src/sync/status-tracker.ts
55
+ var STORAGE_KEY_COMPLETED_TRANSACTIONS = "@pol-powersync:completed_transactions";
56
+ var STORAGE_KEY_FAILED_TRANSACTIONS = "@pol-powersync:failed_transactions";
54
57
  var SyncStatusTracker = class _SyncStatusTracker {
55
58
  storage;
56
59
  logger;
@@ -64,6 +67,13 @@ var SyncStatusTracker = class _SyncStatusTracker {
64
67
  _syncModeListeners = /* @__PURE__ */ new Set();
65
68
  // Force next upload flag for "Sync Now" functionality
66
69
  _forceNextUpload = false;
70
+ // Network reachability gate - blocks uploads instantly when network is unreachable
71
+ _networkReachable = true;
72
+ _networkRestoreTimer = null;
73
+ _networkRestoreDelayMs = 1500;
74
+ // 1.5 seconds delay before restoring
75
+ // Debounce timer for persist operations to avoid race conditions
76
+ _persistDebounceTimer = null;
67
77
  // Track download progress separately to preserve it when offline
68
78
  _lastProgress = null;
69
79
  // Failed transaction tracking
@@ -72,10 +82,15 @@ var SyncStatusTracker = class _SyncStatusTracker {
72
82
  _failureTTLMs = 24 * 60 * 60 * 1e3;
73
83
  // 24 hours
74
84
  _failureListeners = /* @__PURE__ */ new Set();
75
- // Completed transaction tracking
85
+ // Completed transaction tracking (no limit - full audit trail)
76
86
  _completedTransactions = [];
77
- _maxCompletedHistory = 20;
78
87
  _completedListeners = /* @__PURE__ */ new Set();
88
+ // Track when notifications were last displayed/dismissed for "auto-dismiss on display"
89
+ // This allows filtering completed transactions to only show new ones since last display
90
+ _lastNotificationTime = Date.now();
91
+ // Auto-offline flag: tracks whether offline mode was set automatically (network loss)
92
+ // vs manually (user chose offline). Persisted so auto-restore works after app restart.
93
+ _isAutoOffline = false;
79
94
  constructor(storage, logger, options = {}) {
80
95
  this.storage = storage;
81
96
  this.logger = logger;
@@ -114,6 +129,53 @@ var SyncStatusTracker = class _SyncStatusTracker {
114
129
  } catch (err) {
115
130
  this.logger.warn("[StatusTracker] Failed to load sync mode:", err);
116
131
  }
132
+ try {
133
+ const autoOfflineValue = await this.storage.getItem(STORAGE_KEY_AUTO_OFFLINE);
134
+ this._isAutoOffline = autoOfflineValue === "true";
135
+ this.logger.debug("[StatusTracker] Loaded isAutoOffline:", this._isAutoOffline);
136
+ } catch (err) {
137
+ this.logger.warn("[StatusTracker] Failed to load auto-offline flag:", err);
138
+ }
139
+ try {
140
+ const completedJson = await this.storage.getItem(STORAGE_KEY_COMPLETED_TRANSACTIONS);
141
+ if (completedJson) {
142
+ const parsed = JSON.parse(completedJson);
143
+ this._completedTransactions = parsed.map((item) => {
144
+ const remappedEntries = item.entries.map((e) => this.remapEntry(e)).filter((e) => e !== null);
145
+ return {
146
+ ...item,
147
+ completedAt: new Date(item.completedAt),
148
+ entries: remappedEntries
149
+ };
150
+ }).filter((item) => !isNaN(item.completedAt.getTime()) && item.entries.length > 0);
151
+ this.logger.debug("[StatusTracker] Loaded", this._completedTransactions.length, "completed transactions");
152
+ }
153
+ } catch (err) {
154
+ this.logger.warn("[StatusTracker] Failed to load completed transactions:", err);
155
+ }
156
+ try {
157
+ const failedJson = await this.storage.getItem(STORAGE_KEY_FAILED_TRANSACTIONS);
158
+ if (failedJson) {
159
+ const parsed = JSON.parse(failedJson);
160
+ this._failedTransactions = parsed.map((item) => {
161
+ const remappedEntries = item.entries.map((e) => this.remapEntry(e)).filter((e) => e !== null);
162
+ return {
163
+ ...item,
164
+ firstFailedAt: new Date(item.firstFailedAt),
165
+ lastFailedAt: new Date(item.lastFailedAt),
166
+ error: {
167
+ ...item.error,
168
+ timestamp: new Date(item.error.timestamp)
169
+ },
170
+ entries: remappedEntries
171
+ };
172
+ }).filter((item) => !isNaN(item.firstFailedAt.getTime()) && !isNaN(item.lastFailedAt.getTime()) && item.entries.length > 0);
173
+ this.logger.debug("[StatusTracker] Loaded", this._failedTransactions.length, "failed transactions");
174
+ }
175
+ } catch (err) {
176
+ this.logger.warn("[StatusTracker] Failed to load failed transactions:", err);
177
+ }
178
+ this.cleanupStaleFailures();
117
179
  }
118
180
  /**
119
181
  * Dispose the tracker and clear timers.
@@ -123,6 +185,14 @@ var SyncStatusTracker = class _SyncStatusTracker {
123
185
  clearTimeout(this._notifyTimer);
124
186
  this._notifyTimer = null;
125
187
  }
188
+ if (this._persistDebounceTimer) {
189
+ clearTimeout(this._persistDebounceTimer);
190
+ this._persistDebounceTimer = null;
191
+ }
192
+ if (this._networkRestoreTimer) {
193
+ clearTimeout(this._networkRestoreTimer);
194
+ this._networkRestoreTimer = null;
195
+ }
126
196
  this._listeners.clear();
127
197
  this._syncModeListeners.clear();
128
198
  this._failureListeners.clear();
@@ -156,17 +226,10 @@ var SyncStatusTracker = class _SyncStatusTracker {
156
226
  return this._state.syncMode;
157
227
  }
158
228
  /**
159
- * Get whether sync is paused (offline mode).
160
- * @deprecated Use getSyncMode() instead
161
- */
162
- isPaused() {
163
- return this._state.syncMode === "offline";
164
- }
165
- /**
166
- * Check if uploads are allowed based on current sync mode.
229
+ * Check if uploads are allowed based on current sync mode and network reachability.
167
230
  */
168
231
  canUpload() {
169
- return this._state.syncMode === "push-pull";
232
+ return this._networkReachable && this._state.syncMode === "push-pull";
170
233
  }
171
234
  /**
172
235
  * Check if downloads are allowed based on current sync mode.
@@ -192,7 +255,7 @@ var SyncStatusTracker = class _SyncStatusTracker {
192
255
  }
193
256
  }
194
257
  /**
195
- * Check if upload should proceed, considering force flag.
258
+ * Check if upload should proceed, considering force flag and network reachability.
196
259
  * NOTE: Does NOT auto-reset the flag - caller must use clearForceNextUpload()
197
260
  * after all uploads are complete. This prevents race conditions when
198
261
  * PowerSync calls uploadData() multiple times for multiple transactions.
@@ -201,8 +264,43 @@ var SyncStatusTracker = class _SyncStatusTracker {
201
264
  if (this._forceNextUpload) {
202
265
  return true;
203
266
  }
267
+ if (!this._networkReachable) {
268
+ return false;
269
+ }
204
270
  return this._state.syncMode === "push-pull";
205
271
  }
272
+ /**
273
+ * Set network reachability state.
274
+ * - When unreachable: Instantly blocks uploads (0ms)
275
+ * - When reachable: Delayed restore (1-2 seconds) to avoid flickering on brief disconnects
276
+ */
277
+ setNetworkReachable(reachable) {
278
+ if (this._networkRestoreTimer) {
279
+ clearTimeout(this._networkRestoreTimer);
280
+ this._networkRestoreTimer = null;
281
+ }
282
+ if (!reachable) {
283
+ if (this._networkReachable) {
284
+ this._networkReachable = false;
285
+ this.logger.debug("[StatusTracker] Network unreachable - uploads blocked instantly");
286
+ }
287
+ } else {
288
+ if (!this._networkReachable) {
289
+ this.logger.debug("[StatusTracker] Network reachable - scheduling delayed restore");
290
+ this._networkRestoreTimer = setTimeout(() => {
291
+ this._networkRestoreTimer = null;
292
+ this._networkReachable = true;
293
+ this.logger.debug("[StatusTracker] Network restored - uploads enabled");
294
+ }, this._networkRestoreDelayMs);
295
+ }
296
+ }
297
+ }
298
+ /**
299
+ * Get current network reachability state.
300
+ */
301
+ isNetworkReachable() {
302
+ return this._networkReachable;
303
+ }
206
304
  /**
207
305
  * Get pending mutations.
208
306
  */
@@ -290,12 +388,27 @@ var SyncStatusTracker = class _SyncStatusTracker {
290
388
  }
291
389
  this._notifyListeners(true);
292
390
  }
391
+ // ─── Auto-Offline Management ──────────────────────────────────────────────
293
392
  /**
294
- * Set paused state.
295
- * @deprecated Use setSyncMode() instead
393
+ * Get whether offline mode was set automatically (network loss) vs manually.
394
+ * Used to determine if sync should auto-resume when network returns.
296
395
  */
297
- async setPaused(paused) {
298
- await this.setSyncMode(paused ? "offline" : "push-pull");
396
+ getIsAutoOffline() {
397
+ return this._isAutoOffline;
398
+ }
399
+ /**
400
+ * Set the auto-offline flag and persist it.
401
+ * @param isAuto - true if offline was set automatically, false if user chose offline
402
+ */
403
+ async setIsAutoOffline(isAuto) {
404
+ if (this._isAutoOffline === isAuto) return;
405
+ this._isAutoOffline = isAuto;
406
+ try {
407
+ await this.storage.setItem(STORAGE_KEY_AUTO_OFFLINE, isAuto ? "true" : "false");
408
+ this.logger.debug("[StatusTracker] Auto-offline flag changed:", isAuto);
409
+ } catch (err) {
410
+ this.logger.warn("[StatusTracker] Failed to persist auto-offline flag:", err);
411
+ }
299
412
  }
300
413
  // ─── Subscriptions ─────────────────────────────────────────────────────────
301
414
  /**
@@ -320,30 +433,18 @@ var SyncStatusTracker = class _SyncStatusTracker {
320
433
  this._syncModeListeners.delete(listener);
321
434
  };
322
435
  }
323
- /**
324
- * Subscribe to paused state changes.
325
- * @deprecated Use onSyncModeChange() instead
326
- * @returns Unsubscribe function
327
- */
328
- onPausedChange(listener) {
329
- const wrappedListener = (mode) => {
330
- listener(mode === "offline");
331
- };
332
- this._syncModeListeners.add(wrappedListener);
333
- listener(this._state.syncMode === "offline");
334
- return () => {
335
- this._syncModeListeners.delete(wrappedListener);
336
- };
337
- }
338
436
  // ─── Failed Transaction Tracking ────────────────────────────────────────────
339
437
  /**
340
438
  * Record a transaction failure.
341
439
  * If a failure for the same entries already exists, updates the retry count.
342
440
  * Otherwise, creates a new failure record.
441
+ *
442
+ * @param preserveMetadata - Optional. If provided, preserves retryCount and firstFailedAt from a previous failure.
343
443
  */
344
- recordTransactionFailure(entries, error, isPermanent, affectedEntityIds, affectedTables) {
444
+ recordTransactionFailure(entries, error, isPermanent, affectedEntityIds, affectedTables, preserveMetadata) {
345
445
  const now = /* @__PURE__ */ new Date();
346
- const entryIds = entries.map((e) => e.id).sort().join(",");
446
+ const normalizedEntries = this.normalizeEntries(entries);
447
+ const entryIds = normalizedEntries.map((e) => e.id).sort().join(",");
347
448
  const existingIndex = this._failedTransactions.findIndex((f) => {
348
449
  const existingIds = f.entries.map((e) => e.id).sort().join(",");
349
450
  return existingIds === entryIds;
@@ -359,11 +460,11 @@ var SyncStatusTracker = class _SyncStatusTracker {
359
460
  };
360
461
  } else {
361
462
  const newFailure = {
362
- id: generateFailureId(entries),
363
- entries,
463
+ id: generateFailureId(normalizedEntries),
464
+ entries: normalizedEntries,
364
465
  error,
365
- retryCount: 1,
366
- firstFailedAt: now,
466
+ retryCount: preserveMetadata?.retryCount ?? 1,
467
+ firstFailedAt: preserveMetadata?.firstFailedAt ?? now,
367
468
  lastFailedAt: now,
368
469
  isPermanent,
369
470
  affectedEntityIds,
@@ -375,6 +476,7 @@ var SyncStatusTracker = class _SyncStatusTracker {
375
476
  this._failedTransactions = this._failedTransactions.slice(-this._maxStoredFailures);
376
477
  }
377
478
  }
479
+ this._schedulePersist();
378
480
  this._notifyFailureListeners();
379
481
  this._notifyListeners();
380
482
  }
@@ -385,6 +487,7 @@ var SyncStatusTracker = class _SyncStatusTracker {
385
487
  const initialLength = this._failedTransactions.length;
386
488
  this._failedTransactions = this._failedTransactions.filter((f) => f.id !== failureId);
387
489
  if (this._failedTransactions.length !== initialLength) {
490
+ this._schedulePersist();
388
491
  this._notifyFailureListeners();
389
492
  this._notifyListeners();
390
493
  }
@@ -395,9 +498,32 @@ var SyncStatusTracker = class _SyncStatusTracker {
395
498
  clearAllFailures() {
396
499
  if (this._failedTransactions.length === 0) return;
397
500
  this._failedTransactions = [];
501
+ this._schedulePersist();
398
502
  this._notifyFailureListeners();
399
503
  this._notifyListeners();
400
504
  }
505
+ /**
506
+ * Remove a failed transaction from tracking and return its entries.
507
+ * This is a "pop" operation - the failure is removed from the list.
508
+ *
509
+ * Note: The actual CRUD entries remain in PowerSync's ps_crud table
510
+ * until successfully uploaded. This just removes from our tracking.
511
+ *
512
+ * @param failureId - The failure ID to remove
513
+ * @returns The CrudEntry[] that were in the failure, or null if not found
514
+ */
515
+ takeFailureForRetry(failureId) {
516
+ const failure = this._failedTransactions.find((f) => f.id === failureId);
517
+ if (!failure) {
518
+ this.logger.warn("[StatusTracker] Failure not found for retry:", failureId);
519
+ return null;
520
+ }
521
+ this._failedTransactions = this._failedTransactions.filter((f) => f.id !== failureId);
522
+ this._notifyFailureListeners();
523
+ this._schedulePersist();
524
+ this.logger.info("[StatusTracker] Retrieved failure for retry:", failureId, "entries:", failure.entries.length);
525
+ return failure.entries;
526
+ }
401
527
  /**
402
528
  * Get failures affecting a specific entity.
403
529
  */
@@ -442,6 +568,7 @@ var SyncStatusTracker = class _SyncStatusTracker {
442
568
  this._failedTransactions = this._failedTransactions.filter((f) => f.lastFailedAt.getTime() > cutoff);
443
569
  if (this._failedTransactions.length !== initialLength) {
444
570
  this.logger.debug(`[StatusTracker] Cleaned up ${initialLength - this._failedTransactions.length} stale failures`);
571
+ this._schedulePersist();
445
572
  this._notifyFailureListeners();
446
573
  this._notifyListeners();
447
574
  }
@@ -452,20 +579,19 @@ var SyncStatusTracker = class _SyncStatusTracker {
452
579
  * Creates a CompletedTransaction record and adds it to history.
453
580
  */
454
581
  recordTransactionComplete(entries) {
455
- const affectedTables = [...new Set(entries.map((e) => e.table))];
456
- const affectedEntityIds = [...new Set(entries.map((e) => e.id))];
582
+ const normalizedEntries = this.normalizeEntries(entries);
583
+ const affectedTables = [...new Set(normalizedEntries.map((e) => e.table))];
584
+ const affectedEntityIds = [...new Set(normalizedEntries.map((e) => e.id))];
457
585
  const id = `completed_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
458
586
  const completed = {
459
587
  id,
460
- entries,
588
+ entries: normalizedEntries,
461
589
  completedAt: /* @__PURE__ */ new Date(),
462
590
  affectedTables,
463
591
  affectedEntityIds
464
592
  };
465
593
  this._completedTransactions.unshift(completed);
466
- if (this._completedTransactions.length > this._maxCompletedHistory) {
467
- this._completedTransactions = this._completedTransactions.slice(0, this._maxCompletedHistory);
468
- }
594
+ this._schedulePersist();
469
595
  this._notifyCompletedListeners();
470
596
  this.logger.debug(`[StatusTracker] Recorded completed transaction: ${completed.id} (${entries.length} entries)`);
471
597
  }
@@ -481,9 +607,22 @@ var SyncStatusTracker = class _SyncStatusTracker {
481
607
  clearCompletedHistory() {
482
608
  if (this._completedTransactions.length === 0) return;
483
609
  this._completedTransactions = [];
610
+ this._schedulePersist();
484
611
  this._notifyCompletedListeners();
485
612
  this.logger.debug("[StatusTracker] Cleared completed transaction history");
486
613
  }
614
+ /**
615
+ * Clear a specific completed transaction by ID.
616
+ */
617
+ clearCompletedItem(completedId) {
618
+ const initialLength = this._completedTransactions.length;
619
+ this._completedTransactions = this._completedTransactions.filter((c) => c.id !== completedId);
620
+ if (this._completedTransactions.length !== initialLength) {
621
+ this._schedulePersist();
622
+ this._notifyCompletedListeners();
623
+ this.logger.debug("[StatusTracker] Cleared completed transaction:", completedId);
624
+ }
625
+ }
487
626
  /**
488
627
  * Subscribe to completed transaction changes.
489
628
  * @returns Unsubscribe function
@@ -495,19 +634,78 @@ var SyncStatusTracker = class _SyncStatusTracker {
495
634
  this._completedListeners.delete(listener);
496
635
  };
497
636
  }
637
+ // ─── Notification Tracking ─────────────────────────────────────────────────
638
+ /**
639
+ * Get completed transactions that occurred AFTER the last notification time.
640
+ * This is used for displaying "X changes synced" notifications to avoid
641
+ * showing stale counts from historical completed transactions.
642
+ */
643
+ getNewCompletedTransactions() {
644
+ return this._completedTransactions.filter((tx) => tx.completedAt.getTime() > this._lastNotificationTime);
645
+ }
646
+ /**
647
+ * Mark notifications as seen by updating the last notification time.
648
+ * Call this when the notification is displayed or dismissed.
649
+ */
650
+ markNotificationsAsSeen() {
651
+ this._lastNotificationTime = Date.now();
652
+ this.logger.debug("[StatusTracker] Notifications marked as seen");
653
+ this._notifyCompletedListeners();
654
+ }
655
+ /**
656
+ * Get the timestamp of when notifications were last displayed/dismissed.
657
+ */
658
+ getLastNotificationTime() {
659
+ return this._lastNotificationTime;
660
+ }
498
661
  // ─── Private Methods ───────────────────────────────────────────────────────
662
+ /**
663
+ * Schedule a debounced persist operation.
664
+ * This prevents race conditions from multiple rapid persist calls.
665
+ */
666
+ _schedulePersist() {
667
+ if (this._persistDebounceTimer) {
668
+ clearTimeout(this._persistDebounceTimer);
669
+ }
670
+ this._persistDebounceTimer = setTimeout(() => {
671
+ this._persistDebounceTimer = null;
672
+ this._persistTransactions();
673
+ }, 100);
674
+ }
675
+ /**
676
+ * Persist completed and failed transactions to storage.
677
+ */
678
+ async _persistTransactions() {
679
+ try {
680
+ await Promise.all([this.storage.setItem(STORAGE_KEY_COMPLETED_TRANSACTIONS, JSON.stringify(this._completedTransactions)), this.storage.setItem(STORAGE_KEY_FAILED_TRANSACTIONS, JSON.stringify(this._failedTransactions))]);
681
+ } catch (err) {
682
+ this.logger.warn("[StatusTracker] Failed to persist transactions:", err);
683
+ }
684
+ }
499
685
  _hasStatusChanged(newStatus) {
500
686
  const old = this._state.status;
501
687
  return old.connected !== newStatus.connected || old.connecting !== newStatus.connecting || old.hasSynced !== newStatus.hasSynced || old.uploading !== newStatus.uploading || old.downloading !== newStatus.downloading || old.lastSyncedAt?.getTime() !== newStatus.lastSyncedAt?.getTime() || old.downloadProgress?.current !== newStatus.downloadProgress?.current || old.downloadProgress?.target !== newStatus.downloadProgress?.target;
502
688
  }
689
+ /**
690
+ * Notify all listeners of status changes with throttling.
691
+ *
692
+ * Uses a "dirty" flag pattern: when throttled, we schedule a timer
693
+ * but get the CURRENT state when the timer fires, not the stale state
694
+ * from when the timer was scheduled. This ensures rapid state changes
695
+ * during the throttle window aren't lost.
696
+ */
503
697
  _notifyListeners(forceImmediate = false) {
504
698
  const now = Date.now();
505
699
  const timeSinceLastNotify = now - this._lastNotifyTime;
700
+ if (this._notifyTimer && !forceImmediate) {
701
+ return;
702
+ }
506
703
  if (this._notifyTimer) {
507
704
  clearTimeout(this._notifyTimer);
508
705
  this._notifyTimer = null;
509
706
  }
510
707
  const notify = () => {
708
+ this._notifyTimer = null;
511
709
  this._lastNotifyTime = Date.now();
512
710
  const status = this.getStatus();
513
711
  this.onStatusChange?.(status);
@@ -522,8 +720,8 @@ var SyncStatusTracker = class _SyncStatusTracker {
522
720
  if (forceImmediate || timeSinceLastNotify >= this.notifyThrottleMs) {
523
721
  notify();
524
722
  } else {
525
- const delay = this.notifyThrottleMs - timeSinceLastNotify;
526
- this._notifyTimer = setTimeout(notify, delay);
723
+ const delayMs = this.notifyThrottleMs - timeSinceLastNotify;
724
+ this._notifyTimer = setTimeout(notify, delayMs);
527
725
  }
528
726
  }
529
727
  _notifyFailureListeners() {
@@ -546,6 +744,46 @@ var SyncStatusTracker = class _SyncStatusTracker {
546
744
  }
547
745
  }
548
746
  }
747
+ /**
748
+ * Remap a CrudEntry from persisted JSON (handles toJSON() property remapping).
749
+ * PowerSync's CrudEntry.toJSON() remaps: opData→data, table→type, clientId→op_id, transactionId→tx_id
750
+ *
751
+ * @returns The remapped CrudEntry, or null if critical fields (table, id) are missing
752
+ */
753
+ remapEntry(entry) {
754
+ const table = entry.table ?? entry.type;
755
+ const id = entry.id;
756
+ if (!table || typeof table !== "string") {
757
+ this.logger.warn("[StatusTracker] Invalid CrudEntry: missing or invalid table field", entry);
758
+ return null;
759
+ }
760
+ if (!id || typeof id !== "string") {
761
+ this.logger.warn("[StatusTracker] Invalid CrudEntry: missing or invalid id field", entry);
762
+ return null;
763
+ }
764
+ return {
765
+ id,
766
+ clientId: entry.clientId ?? entry.op_id ?? 0,
767
+ op: entry.op,
768
+ table,
769
+ opData: entry.opData ?? entry.data,
770
+ transactionId: entry.transactionId ?? entry.tx_id
771
+ };
772
+ }
773
+ /**
774
+ * Normalize CrudEntry array to plain objects to avoid CrudEntry.toJSON() remapping issues.
775
+ * PowerSync's CrudEntry.toJSON() remaps property names which breaks deserialization.
776
+ */
777
+ normalizeEntries(entries) {
778
+ return entries.map((e) => ({
779
+ id: e.id,
780
+ clientId: e.clientId,
781
+ op: e.op,
782
+ table: e.table,
783
+ opData: e.opData,
784
+ transactionId: e.transactionId
785
+ }));
786
+ }
549
787
  };
550
788
 
551
789
  // src/sync/metrics-collector.ts
@@ -779,6 +1017,7 @@ var HealthMonitor = class {
779
1017
  _listeners = /* @__PURE__ */ new Set();
780
1018
  _running = false;
781
1019
  _paused = false;
1020
+ _pendingTimers = /* @__PURE__ */ new Set();
782
1021
  constructor(logger, options = {}) {
783
1022
  this.logger = logger;
784
1023
  this.checkIntervalMs = options.checkIntervalMs ?? HEALTH_CHECK_INTERVAL_MS;
@@ -813,10 +1052,14 @@ var HealthMonitor = class {
813
1052
  if (this._running) return;
814
1053
  this.logger.info("[HealthMonitor] Starting");
815
1054
  this._running = true;
816
- this._checkHealth();
1055
+ this._checkHealth().catch((err) => {
1056
+ this.logger.warn("[HealthMonitor] Initial check error:", err);
1057
+ });
817
1058
  this._intervalId = setInterval(() => {
818
1059
  if (!this._paused) {
819
- this._checkHealth();
1060
+ this._checkHealth().catch((err) => {
1061
+ this.logger.warn("[HealthMonitor] Periodic check error:", err);
1062
+ });
820
1063
  }
821
1064
  }, this.checkIntervalMs);
822
1065
  }
@@ -837,6 +1080,8 @@ var HealthMonitor = class {
837
1080
  */
838
1081
  pause() {
839
1082
  this._paused = true;
1083
+ this._pendingTimers.forEach(clearTimeout);
1084
+ this._pendingTimers.clear();
840
1085
  this._updateHealth({
841
1086
  ...this._health,
842
1087
  status: "disconnected"
@@ -847,13 +1092,17 @@ var HealthMonitor = class {
847
1092
  */
848
1093
  resume() {
849
1094
  this._paused = false;
850
- this._checkHealth();
1095
+ this._checkHealth().catch((err) => {
1096
+ this.logger.warn("[HealthMonitor] Resume check error:", err);
1097
+ });
851
1098
  }
852
1099
  /**
853
1100
  * Dispose the monitor and clear all resources.
854
1101
  */
855
1102
  dispose() {
856
1103
  this.stop();
1104
+ this._pendingTimers.forEach(clearTimeout);
1105
+ this._pendingTimers.clear();
857
1106
  this._listeners.clear();
858
1107
  }
859
1108
  // ─── Getters ───────────────────────────────────────────────────────────────
@@ -982,12 +1231,16 @@ var HealthMonitor = class {
982
1231
  _withTimeout(promise, timeoutMs) {
983
1232
  return new Promise((resolve, reject) => {
984
1233
  const timer = setTimeout(() => {
1234
+ this._pendingTimers.delete(timer);
985
1235
  reject(new Error(`Health check timeout after ${timeoutMs}ms`));
986
1236
  }, timeoutMs);
1237
+ this._pendingTimers.add(timer);
987
1238
  promise.then((result) => {
1239
+ this._pendingTimers.delete(timer);
988
1240
  clearTimeout(timer);
989
1241
  resolve(result);
990
1242
  }, (error) => {
1243
+ this._pendingTimers.delete(timer);
991
1244
  clearTimeout(timer);
992
1245
  reject(error);
993
1246
  });
@@ -1004,4 +1257,4 @@ export {
1004
1257
  MetricsCollector,
1005
1258
  HealthMonitor
1006
1259
  };
1007
- //# sourceMappingURL=chunk-R4YFWQ3Q.js.map
1260
+ //# sourceMappingURL=chunk-H772V6XQ.js.map