@pol-studios/powersync 1.0.1 → 1.0.3
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.
- package/dist/attachments/index.js +1 -1
- package/dist/{chunk-PANEMMTU.js → chunk-3AYXHQ4W.js} +17 -11
- package/dist/chunk-3AYXHQ4W.js.map +1 -0
- package/dist/{chunk-BJ36QDFN.js → chunk-7EMDVIZX.js} +1 -1
- package/dist/chunk-7EMDVIZX.js.map +1 -0
- package/dist/{chunk-MB2RC3NS.js → chunk-C2RSTGDC.js} +129 -89
- package/dist/chunk-C2RSTGDC.js.map +1 -0
- package/dist/{chunk-NPNBGCRC.js → chunk-EJ23MXPQ.js} +1 -1
- package/dist/{chunk-NPNBGCRC.js.map → chunk-EJ23MXPQ.js.map} +1 -1
- package/dist/{chunk-CHRTN5PF.js → chunk-FPTDATY5.js} +1 -1
- package/dist/chunk-FPTDATY5.js.map +1 -0
- package/dist/chunk-GMFDCVMZ.js +1285 -0
- package/dist/chunk-GMFDCVMZ.js.map +1 -0
- package/dist/chunk-OLHGI472.js +1 -0
- package/dist/chunk-OLHGI472.js.map +1 -0
- package/dist/{chunk-CFCK2LHI.js → chunk-OTJXIRWX.js} +45 -40
- package/dist/chunk-OTJXIRWX.js.map +1 -0
- package/dist/{chunk-GBGATW2S.js → chunk-V6LJ6MR2.js} +86 -95
- package/dist/chunk-V6LJ6MR2.js.map +1 -0
- package/dist/connector/index.d.ts +1 -1
- package/dist/connector/index.js +2 -2
- package/dist/core/index.js +2 -2
- package/dist/{index-D952Qr38.d.ts → index-Cb-NI0Ct.d.ts} +9 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +8 -9
- package/dist/index.native.d.ts +1 -1
- package/dist/index.native.js +9 -10
- package/dist/index.web.d.ts +1 -1
- package/dist/index.web.js +9 -10
- package/dist/platform/index.js.map +1 -1
- package/dist/platform/index.native.js +1 -1
- package/dist/platform/index.web.js +1 -1
- package/dist/provider/index.d.ts +6 -1
- package/dist/provider/index.js +6 -6
- package/dist/sync/index.js +3 -3
- package/package.json +3 -1
- package/dist/chunk-42IJ25Q4.js +0 -45
- package/dist/chunk-42IJ25Q4.js.map +0 -1
- package/dist/chunk-BJ36QDFN.js.map +0 -1
- package/dist/chunk-CFCK2LHI.js.map +0 -1
- package/dist/chunk-CHRTN5PF.js.map +0 -1
- package/dist/chunk-GBGATW2S.js.map +0 -1
- package/dist/chunk-H7HZMI4H.js +0 -925
- package/dist/chunk-H7HZMI4H.js.map +0 -1
- package/dist/chunk-MB2RC3NS.js.map +0 -1
- package/dist/chunk-PANEMMTU.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/provider/context.ts","../src/provider/PowerSyncProvider.tsx","../src/conflicts/conflict-bus.ts","../src/provider/hooks.ts"],"sourcesContent":["/**\n * React Contexts for @pol-studios/powersync\n *\n * This module creates the React contexts used by the PowerSyncProvider.\n */\n\nimport { createContext } from 'react';\nimport type { PowerSyncContextValue, SyncStatusContextValue, ConnectionHealthContextValue, SyncMetricsContextValue, DEFAULT_SYNC_STATUS, DEFAULT_CONNECTION_HEALTH, DEFAULT_SYNC_METRICS } from './types';\nimport type { AttachmentQueue } from '../attachments/attachment-queue';\n\n// ─── Main PowerSync Context ──────────────────────────────────────────────────\n\n/**\n * Main context for PowerSync database instance and related state.\n *\n * Provides access to:\n * - PowerSync database instance\n * - SupabaseConnector instance\n * - AttachmentQueue instance (if configured)\n * - Initialization state\n *\n * @example\n * ```typescript\n * const { db, isReady, error } = useContext(PowerSyncContext);\n * ```\n */\nexport const PowerSyncContext = createContext<PowerSyncContextValue | null>(null);\nPowerSyncContext.displayName = 'PowerSyncContext';\n\n// ─── Sync Status Context ─────────────────────────────────────────────────────\n\n/**\n * Context for sync status updates.\n *\n * Provides access to:\n * - Current sync status (connected, syncing, etc.)\n * - Pending mutations count\n * - Paused state\n * - Last synced timestamp\n *\n * @example\n * ```typescript\n * const { status, pendingCount, isPaused } = useContext(SyncStatusContext);\n * ```\n */\nexport const SyncStatusContext = createContext<SyncStatusContextValue | null>(null);\nSyncStatusContext.displayName = 'SyncStatusContext';\n\n// ─── Connection Health Context ───────────────────────────────────────────────\n\n/**\n * Context for connection health monitoring.\n *\n * Provides access to:\n * - Health status (healthy, degraded, disconnected)\n * - Latency measurements\n * - Health check timestamps\n * - Failure counts\n *\n * @example\n * ```typescript\n * const { health } = useContext(ConnectionHealthContext);\n * if (health.status === 'degraded') {\n * showWarning('Connection is slow');\n * }\n * ```\n */\nexport const ConnectionHealthContext = createContext<ConnectionHealthContextValue | null>(null);\nConnectionHealthContext.displayName = 'ConnectionHealthContext';\n\n// ─── Sync Metrics Context ────────────────────────────────────────────────────\n\n/**\n * Context for sync metrics and statistics.\n *\n * Provides access to:\n * - Sync operation counts\n * - Sync durations\n * - Data transfer amounts\n * - Error tracking\n *\n * @example\n * ```typescript\n * const { metrics } = useContext(SyncMetricsContext);\n * console.log(`Total syncs: ${metrics.totalSyncs}`);\n * ```\n */\nexport const SyncMetricsContext = createContext<SyncMetricsContextValue | null>(null);\nSyncMetricsContext.displayName = 'SyncMetricsContext';\n\n// ─── Attachment Queue Context ────────────────────────────────────────────────\n\n/**\n * Context for the attachment queue (if configured).\n *\n * Provides direct access to the AttachmentQueue instance for:\n * - Checking attachment sync stats\n * - Pausing/resuming downloads\n * - Getting local URIs for attachments\n *\n * @example\n * ```typescript\n * const attachmentQueue = useContext(AttachmentQueueContext);\n * if (attachmentQueue) {\n * const stats = await attachmentQueue.getStats();\n * console.log(`Downloaded: ${stats.syncedCount}/${stats.totalExpected}`);\n * }\n * ```\n */\nexport const AttachmentQueueContext = createContext<AttachmentQueue | null>(null);\nAttachmentQueueContext.displayName = 'AttachmentQueueContext';","/**\n * PowerSyncProvider Component for @pol-studios/powersync\n *\n * Main provider component that initializes and manages the PowerSync database,\n * connector, attachment queue, and all monitoring services.\n */\n\nimport React, { useEffect, useState, useRef, useMemo, useCallback } from 'react';\nimport type { Session } from '@supabase/supabase-js';\nimport type { AbstractPowerSyncDatabase, SyncStatus, ConnectionHealth, SyncMetrics, CrudEntry, FailedTransaction, CompletedTransaction, SyncMode } from '../core/types';\nimport { SupabaseConnector } from '../connector/supabase-connector';\nimport { extractEntityIds, extractTableNames, createSyncError } from '../core/errors';\nimport { AttachmentQueue } from '../attachments/attachment-queue';\nimport { SyncStatusTracker } from '../sync/status-tracker';\nimport { MetricsCollector } from '../sync/metrics-collector';\nimport { HealthMonitor } from '../sync/health-monitor';\nimport { ConflictBus } from '../conflicts/conflict-bus';\nimport { PowerSyncContext, SyncStatusContext, ConnectionHealthContext, SyncMetricsContext, AttachmentQueueContext } from './context';\nimport type { PowerSyncProviderProps, PowerSyncContextValue, SyncStatusContextValue, ConnectionHealthContextValue, SyncMetricsContextValue } from './types';\nimport { DEFAULT_SYNC_STATUS, DEFAULT_CONNECTION_HEALTH, DEFAULT_SYNC_METRICS } from './types';\n\n/**\n * PowerSyncProvider initializes and manages the PowerSync database and related services.\n *\n * Features:\n * - Initializes PowerSync database using platform adapter\n * - Creates and manages SupabaseConnector\n * - Connects/disconnects based on auth state\n * - Tracks sync status and connection health\n * - Optionally initializes AttachmentQueue\n * - Provides all contexts to children\n * - Handles cleanup on unmount\n *\n * @example\n * ```tsx\n * import { PowerSyncProvider, usePowerSync, useSyncStatus } from '@pol-studios/powersync';\n *\n * function App() {\n * return (\n * <PowerSyncProvider\n * config={{\n * platform: createNativePlatformAdapter(logger),\n * schema: AppSchema,\n * powerSyncUrl: 'https://your-powersync.com',\n * supabaseClient: supabase,\n * }}\n * onReady={() => console.log('PowerSync ready!')}\n * onError={(err) => console.error('PowerSync error:', err)}\n * >\n * <MainApp />\n * </PowerSyncProvider>\n * );\n * }\n * ```\n */\nexport function PowerSyncProvider<TSchema = unknown>({\n config,\n children,\n onReady,\n onError,\n onSyncStatusChange\n}: PowerSyncProviderProps<TSchema>): React.ReactElement {\n const {\n platform,\n schema,\n powerSyncUrl,\n supabaseClient,\n dbFilename = 'powersync.db',\n connector: connectorConfig,\n attachments: attachmentConfig,\n sync: syncConfig\n } = config;\n const logger = platform.logger;\n\n // Merge sync config with defaults\n const mergedSyncConfig = {\n autoConnect: syncConfig?.autoConnect ?? true,\n syncInterval: syncConfig?.syncInterval ?? 0,\n enableHealthMonitoring: syncConfig?.enableHealthMonitoring ?? true,\n enableMetrics: syncConfig?.enableMetrics ?? true\n };\n\n // ─── State ─────────────────────────────────────────────────────────────────\n\n const [db, setDb] = useState<AbstractPowerSyncDatabase | null>(null);\n const [connector, setConnector] = useState<SupabaseConnector | null>(null);\n const [attachmentQueue, setAttachmentQueue] = useState<AttachmentQueue | null>(null);\n const [isReady, setIsReady] = useState(false);\n const [isInitializing, setIsInitializing] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const [session, setSession] = useState<Session | null>(null);\n\n // Conflict bus - created once and stable for the lifetime of the provider\n const conflictBusRef = useRef<ConflictBus | null>(null);\n if (!conflictBusRef.current) {\n conflictBusRef.current = new ConflictBus();\n }\n const conflictBus = conflictBusRef.current;\n\n // Sync status state\n const [syncStatus, setSyncStatus] = useState<SyncStatus>(DEFAULT_SYNC_STATUS);\n const [pendingMutations, setPendingMutations] = useState<CrudEntry[]>([]);\n // Combined sync mode state to ensure atomic updates and prevent React batching issues\n const [syncModeState, setSyncModeState] = useState<{\n loaded: boolean;\n mode: SyncMode;\n }>({\n loaded: false,\n mode: 'push-pull'\n });\n const [lastSyncedAt, setLastSyncedAt] = useState<Date | null>(null);\n const [connectionError, setConnectionError] = useState<Error | null>(null);\n const [failedTransactions, setFailedTransactions] = useState<FailedTransaction[]>([]);\n const [completedTransactions, setCompletedTransactions] = useState<CompletedTransaction[]>([]);\n\n // Health and metrics state\n const [connectionHealth, setConnectionHealth] = useState<ConnectionHealth>(DEFAULT_CONNECTION_HEALTH);\n const [syncMetrics, setSyncMetrics] = useState<SyncMetrics>(DEFAULT_SYNC_METRICS);\n\n // ─── Refs ──────────────────────────────────────────────────────────────────\n\n const statusTrackerRef = useRef<SyncStatusTracker | null>(null);\n const metricsCollectorRef = useRef<MetricsCollector | null>(null);\n const healthMonitorRef = useRef<HealthMonitor | null>(null);\n const attachmentQueueRef = useRef<AttachmentQueue | null>(null);\n const listenerUnsubscribeRef = useRef<(() => void) | null>(null);\n const wasSyncingRef = useRef(false);\n const initializingRef = useRef(false);\n // Track when database is being/has been closed to prevent operations on closed db\n const dbClosedRef = useRef(false);\n // Store connector to clean up on recreation\n const connectorRef = useRef<SupabaseConnector | null>(null);\n\n // ─── Initialize Monitoring Services ────────────────────────────────────────\n\n useEffect(() => {\n // Create status tracker\n const statusTracker = new SyncStatusTracker(platform.storage, logger, {\n onStatusChange: status => {\n setSyncStatus(status);\n setLastSyncedAt(status.lastSyncedAt);\n onSyncStatusChange?.(status);\n }\n });\n statusTrackerRef.current = statusTracker;\n\n // Create metrics collector\n const metricsCollector = new MetricsCollector(platform.storage, logger, {\n onMetricsChange: setSyncMetrics\n });\n metricsCollectorRef.current = metricsCollector;\n\n // Create health monitor\n const healthMonitor = new HealthMonitor(logger, {\n onHealthChange: setConnectionHealth\n });\n healthMonitorRef.current = healthMonitor;\n\n // Initialize async with timeout fallback\n const initPromise = Promise.all([statusTracker.init(), metricsCollector.init()]);\n const timeoutPromise = new Promise<[void, void]>(resolve => {\n setTimeout(() => {\n logger.warn('[PowerSyncProvider] Sync mode state load timed out, using default (push-pull)');\n resolve([undefined, undefined]);\n }, 5000); // 5 second timeout\n });\n Promise.race([initPromise, timeoutPromise]).then(() => {\n logger.info('[PowerSyncProvider] Sync mode state loaded:', {\n mode: statusTracker.getSyncMode()\n });\n // Use single state update to ensure atomic change and prevent React batching issues\n setSyncModeState({\n loaded: true,\n mode: statusTracker.getSyncMode()\n });\n });\n\n // Cleanup\n return () => {\n statusTracker.dispose();\n metricsCollector.dispose();\n healthMonitor.dispose();\n };\n }, [platform, logger, onSyncStatusChange]);\n\n // ─── Auth State Listener ───────────────────────────────────────────────────\n\n useEffect(() => {\n logger.debug('[PowerSyncProvider] Setting up auth listener');\n\n // Get initial session\n supabaseClient.auth.getSession().then(({\n data: {\n session: initialSession\n }\n }) => {\n logger.debug('[PowerSyncProvider] Initial session:', !!initialSession);\n setSession(initialSession);\n });\n\n // Listen for auth changes\n const {\n data: {\n subscription\n }\n } = supabaseClient.auth.onAuthStateChange((_event, newSession) => {\n logger.debug('[PowerSyncProvider] Auth state changed, hasSession:', !!newSession);\n setSession(newSession);\n });\n return () => {\n subscription.unsubscribe();\n };\n }, [supabaseClient, logger]);\n\n // ─── Database Initialization ───────────────────────────────────────────────\n\n useEffect(() => {\n // Guard against StrictMode double-mounting\n // The ref persists across the quick unmount/remount cycle\n if (initializingRef.current) {\n logger.debug('[PowerSyncProvider] Already initializing, skipping...');\n return;\n }\n initializingRef.current = true;\n\n // Use a controller object to track cancellation\n const controller = {\n cancelled: false\n };\n const initDatabase = async () => {\n try {\n logger.info('[PowerSyncProvider] Initializing database...');\n\n // Create database using platform adapter (includes init())\n const database = await platform.createDatabase({\n dbFilename,\n schema\n });\n\n // Check if cancelled during async operation\n if (controller.cancelled) {\n logger.debug('[PowerSyncProvider] Init cancelled, closing database...');\n await database.close();\n initializingRef.current = false;\n return;\n }\n logger.info('[PowerSyncProvider] Database initialized');\n setDb(database);\n setIsReady(true);\n setIsInitializing(false);\n\n // Set database on health monitor\n healthMonitorRef.current?.setDatabase(database);\n\n // Start health monitoring if enabled\n if (mergedSyncConfig.enableHealthMonitoring) {\n healthMonitorRef.current?.start();\n }\n onReady?.();\n } catch (err) {\n const initError = err instanceof Error ? err : new Error(String(err));\n logger.error('[PowerSyncProvider] Initialization failed:', initError);\n\n // Only update state if not cancelled\n if (!controller.cancelled) {\n setError(initError);\n setIsInitializing(false);\n onError?.(initError);\n }\n\n // Reset ref on error so retry is possible\n initializingRef.current = false;\n }\n };\n initDatabase();\n return () => {\n // Mark as cancelled - the async operation will check this\n controller.cancelled = true;\n // Reset initializingRef so StrictMode double-mount can reinitialize\n initializingRef.current = false;\n };\n }, [platform, dbFilename, schema, logger, mergedSyncConfig.enableHealthMonitoring, onReady, onError]);\n\n // ─── Connect to PowerSync ──────────────────────────────────────────────────\n\n useEffect(() => {\n // Console.log fallback for critical diagnostic point (always visible regardless of logger level)\n if (__DEV__) {\n console.log('[PowerSyncProvider] Connect effect triggered:', {\n hasDb: !!db,\n hasSession: !!session,\n autoConnect: mergedSyncConfig.autoConnect,\n syncModeState\n });\n }\n\n // Use info level for critical connect path so it always appears\n logger.info('[PowerSyncProvider] Connect effect - db:', !!db, 'session:', !!session, 'autoConnect:', mergedSyncConfig.autoConnect, 'syncModeLoaded:', syncModeState.loaded, 'syncMode:', syncModeState.mode);\n\n // Individual checks with logging for each early return condition\n if (!db) {\n logger.debug('[PowerSyncProvider] Connect effect - waiting for db');\n return;\n }\n if (!session) {\n logger.debug('[PowerSyncProvider] Connect effect - no session');\n return;\n }\n if (!mergedSyncConfig.autoConnect) {\n logger.debug('[PowerSyncProvider] Connect effect - autoConnect disabled');\n return;\n }\n\n // Wait for sync mode state to be loaded before deciding\n if (!syncModeState.loaded) {\n logger.info('[PowerSyncProvider] Waiting for sync mode state to load...');\n return;\n }\n\n // Skip connect if offline mode\n if (syncModeState.mode === 'offline') {\n logger.debug('[PowerSyncProvider] Skipping connect - offline mode');\n return;\n }\n const connectPowerSync = async () => {\n try {\n setConnectionError(null);\n\n // Destroy previous connector to clean up listeners\n connectorRef.current?.destroy();\n\n // Create connector with failure callbacks\n const statusTracker_0 = statusTrackerRef.current;\n const newConnector = new SupabaseConnector({\n supabaseClient,\n powerSyncUrl,\n schemaRouter: connectorConfig?.schemaRouter,\n crudHandler: connectorConfig?.crudHandler,\n logger,\n // Conflict detection - enabled by default\n conflictDetection: {\n enabled: true\n },\n conflictBus,\n // Check if uploads should be performed based on sync mode\n shouldUpload: () => statusTrackerRef.current?.shouldUpload() ?? true,\n // Clear failures when transaction succeeds\n onTransactionSuccess: entries => {\n if (!statusTracker_0) return;\n const entityIds = extractEntityIds(entries);\n entityIds.forEach(id => {\n // Find and clear failures affecting this entity\n const failures = statusTracker_0.getFailuresForEntity(id);\n failures.forEach(f => statusTracker_0.clearFailure(f.id));\n });\n // Update local state\n setFailedTransactions(statusTracker_0.getFailedTransactions());\n },\n // Record failures when transaction fails\n onTransactionFailure: (entries_0, error_0, classified) => {\n if (!statusTracker_0) return;\n statusTracker_0.recordTransactionFailure(entries_0, createSyncError(classified, error_0.message), classified.isPermanent, extractEntityIds(entries_0), extractTableNames(entries_0));\n // Update local state\n setFailedTransactions(statusTracker_0.getFailedTransactions());\n },\n // Record completed transactions\n onTransactionComplete: entries_1 => {\n if (!statusTracker_0) return;\n statusTracker_0.recordTransactionComplete(entries_1);\n // Update local state\n setCompletedTransactions(statusTracker_0.getCompletedTransactions());\n }\n });\n setConnector(newConnector);\n connectorRef.current = newConnector;\n\n // Check if already connected\n if (db.connected) {\n logger.debug('[PowerSyncProvider] Already connected, reconnecting...');\n await db.disconnect();\n }\n logger.info('[PowerSyncProvider] Connecting to PowerSync...');\n await db.connect(newConnector);\n logger.info('[PowerSyncProvider] Connected successfully');\n\n // Reset reconnect attempts on successful connection\n healthMonitorRef.current?.resetReconnectAttempts();\n } catch (err_0) {\n const connectError = err_0 instanceof Error ? err_0 : new Error(String(err_0));\n logger.error('[PowerSyncProvider] Connection failed:', connectError);\n setConnectionError(connectError);\n healthMonitorRef.current?.recordReconnectAttempt();\n }\n };\n connectPowerSync();\n }, [db, session, powerSyncUrl, supabaseClient, connectorConfig, conflictBus, mergedSyncConfig.autoConnect, syncModeState, logger]);\n\n // ─── Status Listener ───────────────────────────────────────────────────────\n\n useEffect(() => {\n if (!db) return;\n\n // Set initial status\n const initialStatus = db.currentStatus;\n if (initialStatus) {\n statusTrackerRef.current?.handleStatusChange(initialStatus);\n }\n\n // Register listener\n const unsubscribe = db.registerListener({\n statusChanged: status_0 => {\n statusTrackerRef.current?.handleStatusChange(status_0 as Record<string, unknown>);\n\n // Track sync timing for metrics\n const dataFlow = (status_0 as Record<string, unknown>).dataFlowStatus as Record<string, boolean> | undefined;\n const isDownloading = dataFlow?.downloading ?? false;\n const progress = (status_0 as Record<string, unknown>).downloadProgress as Record<string, number> | undefined;\n if (isDownloading && !wasSyncingRef.current) {\n metricsCollectorRef.current?.markSyncStart();\n }\n if (!isDownloading && wasSyncingRef.current) {\n const duration = metricsCollectorRef.current?.markSyncEnd();\n if (duration !== null && duration !== undefined) {\n metricsCollectorRef.current?.recordSync({\n durationMs: duration,\n success: true,\n operationsDownloaded: progress?.totalOperations ?? 0\n });\n }\n }\n wasSyncingRef.current = isDownloading;\n }\n });\n listenerUnsubscribeRef.current = unsubscribe;\n return () => {\n unsubscribe();\n listenerUnsubscribeRef.current = null;\n };\n }, [db]);\n\n // ─── Watch ps_crud for Real-time Pending Count ─────────────────────────────\n\n // Track previous pending count to detect when all uploads complete\n const prevPendingCountRef = useRef<number>(0);\n\n /**\n * Parse raw ps_crud row into a CrudEntry object.\n * The ps_crud table has columns: id, tx_id, data\n * The data column is a JSON string with: op, type (table name), and operation data\n */\n const parseCrudRow = useCallback((row: {\n id: string;\n tx_id: number | null;\n data: string;\n }): CrudEntry | null => {\n try {\n const parsed = JSON.parse(row.data);\n // Map PowerSync operation types to our CrudEntry op types\n const opMap: Record<string, 'PUT' | 'PATCH' | 'DELETE'> = {\n 'PUT': 'PUT',\n 'PATCH': 'PATCH',\n 'DELETE': 'DELETE'\n };\n const op = opMap[parsed.op] ?? 'PUT';\n\n // Extract entity ID: prefer parsed.data.id (actual record UUID), fallback to parsed.id, then row.id\n const entityId = parsed.data?.id as string ?? parsed.id ?? row.id;\n return {\n id: entityId,\n clientId: parseInt(row.id, 10) || 0,\n op,\n table: parsed.type ?? 'unknown',\n opData: parsed.data ?? undefined,\n // Ensure null becomes undefined\n transactionId: row.tx_id ?? undefined\n };\n } catch (e) {\n logger.warn('[PowerSyncProvider] Failed to parse CRUD entry:', e, row);\n return null;\n }\n }, [logger]);\n useEffect(() => {\n if (!db) return;\n const abortController = new AbortController();\n\n // Watch the ps_crud table to get real-time pending upload count\n // This fires on local mutations, not just sync status changes\n db.watch('SELECT COUNT(*) as count FROM ps_crud', [], {\n onResult: async (results: {\n rows?: {\n _array?: Array<{\n count: number;\n }>;\n };\n }) => {\n // Guard against operations on closed database\n if (dbClosedRef.current) {\n logger.debug('[PowerSyncProvider] Skipping watch callback - database is closed');\n return;\n }\n const count = results.rows?._array?.[0]?.count ?? 0;\n const prevCount = prevPendingCountRef.current;\n prevPendingCountRef.current = count;\n\n // Fetch actual CRUD entries when count > 0\n let mutations: CrudEntry[] = [];\n if (count > 0) {\n try {\n // Double-check database is still open before query\n if (dbClosedRef.current) {\n logger.debug('[PowerSyncProvider] Skipping getAll - database is closed');\n return;\n }\n // Query the actual ps_crud rows to get real data\n const rows = await db.getAll<{\n id: string;\n tx_id: number | null;\n data: string;\n }>('SELECT id, tx_id, data FROM ps_crud ORDER BY id ASC');\n mutations = rows.map(parseCrudRow).filter((entry): entry is CrudEntry => entry !== null);\n } catch (e_0) {\n // Check if this is a \"database is not open\" error and handle gracefully\n const errorMessage = e_0 instanceof Error ? e_0.message : String(e_0);\n if (errorMessage.includes('not open') || errorMessage.includes('closed')) {\n logger.debug('[PowerSyncProvider] Database closed during CRUD fetch, ignoring');\n return;\n }\n logger.warn('[PowerSyncProvider] Failed to fetch CRUD entries:', e_0);\n // Fall back to empty array - count will still be accurate from the watch\n }\n }\n\n // Final check before updating state\n if (dbClosedRef.current) {\n return;\n }\n statusTrackerRef.current?.updatePendingMutations(mutations);\n setPendingMutations(mutations);\n\n // Clear force upload flag when pending count drops to 0\n // This ensures all pending transactions get uploaded during \"Sync Now\"\n // and then the flag is cleared when done\n if (prevCount > 0 && count === 0) {\n logger.debug('[PowerSyncProvider] All pending uploads complete, clearing force flag');\n statusTrackerRef.current?.clearForceNextUpload();\n }\n },\n onError: (error_1: Error) => {\n // Check if this is a \"database is not open\" error and handle gracefully\n const errorMessage_0 = error_1.message ?? '';\n if (errorMessage_0.includes('not open') || errorMessage_0.includes('closed') || dbClosedRef.current) {\n logger.debug('[PowerSyncProvider] Watch error during database close, ignoring');\n return;\n }\n logger.warn('[PowerSyncProvider] Error watching ps_crud:', error_1);\n }\n }, {\n signal: abortController.signal,\n throttleMs: 100 // Debounce to avoid excessive updates during bulk operations\n });\n return () => {\n abortController.abort();\n };\n }, [db, logger, parseCrudRow]);\n\n // ─── Attachment Queue Initialization ───────────────────────────────────────\n\n useEffect(() => {\n if (!db || !attachmentConfig || attachmentQueueRef.current) {\n return;\n }\n const initAttachmentQueue = async () => {\n try {\n logger.info('[PowerSyncProvider] Initializing attachment queue...');\n const queue = new AttachmentQueue({\n powersync: db,\n platform,\n config: attachmentConfig\n });\n await queue.init();\n attachmentQueueRef.current = queue;\n setAttachmentQueue(queue);\n logger.info('[PowerSyncProvider] Attachment queue initialized');\n } catch (err_1) {\n logger.error('[PowerSyncProvider] Attachment queue initialization failed:', err_1);\n }\n };\n initAttachmentQueue();\n return () => {\n attachmentQueueRef.current?.dispose();\n attachmentQueueRef.current = null;\n };\n }, [db, attachmentConfig, platform, logger]);\n\n // ─── Cleanup ───────────────────────────────────────────────────────────────\n\n useEffect(() => {\n // Reset closed flag when db changes (new db instance)\n if (db) {\n dbClosedRef.current = false;\n }\n return () => {\n // Cleanup on unmount\n logger.info('[PowerSyncProvider] Cleaning up...');\n\n // Mark database as closed FIRST to prevent any pending operations\n dbClosedRef.current = true;\n\n // Destroy connector to clean up event listeners\n connectorRef.current?.destroy();\n connectorRef.current = null;\n listenerUnsubscribeRef.current?.();\n attachmentQueueRef.current?.dispose();\n healthMonitorRef.current?.stop();\n conflictBusRef.current?.destroy();\n if (db) {\n // Use async IIFE to properly sequence disconnect and close\n (async () => {\n try {\n await db.disconnect();\n await db.close();\n } catch (err_2) {\n // Only log if it's not a \"database already closed\" error\n const errorMessage_1 = err_2 instanceof Error ? err_2.message : String(err_2);\n if (!errorMessage_1.includes('not open') && !errorMessage_1.includes('closed')) {\n logger.warn('[PowerSyncProvider] Error during cleanup:', err_2);\n }\n }\n })();\n }\n };\n }, [db, logger]);\n\n // ─── Clear Failure Functions ────────────────────────────────────────────────\n\n const clearFailure = useCallback((failureId: string) => {\n const tracker = statusTrackerRef.current;\n if (!tracker) {\n logger.warn('[PowerSyncProvider] Cannot clear failure - tracker not initialized');\n return;\n }\n tracker.clearFailure(failureId);\n setFailedTransactions(tracker.getFailedTransactions());\n }, [logger]);\n const clearAllFailures = useCallback(() => {\n const tracker_0 = statusTrackerRef.current;\n if (!tracker_0) {\n logger.warn('[PowerSyncProvider] Cannot clear failures - tracker not initialized');\n return;\n }\n tracker_0.clearAllFailures();\n setFailedTransactions(tracker_0.getFailedTransactions());\n }, [logger]);\n const clearCompletedHistory = useCallback(() => {\n const tracker_1 = statusTrackerRef.current;\n if (!tracker_1) {\n logger.warn('[PowerSyncProvider] Cannot clear completed history - tracker not initialized');\n return;\n }\n tracker_1.clearCompletedHistory();\n setCompletedTransactions(tracker_1.getCompletedTransactions());\n }, [logger]);\n const setSyncMode = useCallback(async (mode: SyncMode) => {\n const tracker_2 = statusTrackerRef.current;\n if (!tracker_2) {\n logger.warn('[PowerSyncProvider] Cannot set sync mode - tracker not initialized');\n return;\n }\n await tracker_2.setSyncMode(mode);\n setSyncModeState({\n loaded: true,\n mode\n });\n }, [logger]);\n const setForceNextUpload = useCallback((force: boolean) => {\n const tracker_3 = statusTrackerRef.current;\n if (!tracker_3) {\n logger.warn('[PowerSyncProvider] Cannot set force upload - tracker not initialized');\n return;\n }\n tracker_3.setForceNextUpload(force);\n }, [logger]);\n\n // ─── Discard Mutation Functions ────────────────────────────────────────────\n\n const discardPendingMutation = useCallback(async (clientId: number) => {\n if (!db || !connector) {\n logger.warn('[PowerSync] Cannot discard - not initialized');\n return;\n }\n if (syncStatus.uploading) {\n throw new Error('Cannot discard while upload is in progress');\n }\n logger.info('[PowerSync] Discarding pending mutation:', clientId);\n\n // Disconnect to ensure no active transaction\n await db.disconnect();\n try {\n await db.execute('DELETE FROM ps_crud WHERE id = ?', [clientId]);\n logger.info('[PowerSync] Mutation discarded successfully');\n } finally {\n // Always reconnect\n await db.connect(connector);\n }\n }, [db, connector, syncStatus.uploading, logger]);\n const discardAllPendingMutations = useCallback(async () => {\n if (!db || !connector) {\n logger.warn('[PowerSync] Cannot discard all - not initialized');\n return;\n }\n if (syncStatus.uploading) {\n throw new Error('Cannot discard while upload is in progress');\n }\n logger.info('[PowerSync] Discarding all pending mutations');\n await db.disconnect();\n try {\n await db.execute('DELETE FROM ps_crud');\n logger.info('[PowerSync] All mutations discarded successfully');\n } finally {\n await db.connect(connector);\n }\n }, [db, connector, syncStatus.uploading, logger]);\n\n // ─── Context Values ────────────────────────────────────────────────────────\n\n const powerSyncContextValue = useMemo<PowerSyncContextValue<TSchema>>(() => ({\n db,\n connector,\n attachmentQueue,\n isReady,\n isInitializing,\n error,\n schema,\n platform,\n conflictBus\n }), [db, connector, attachmentQueue, isReady, isInitializing, error, schema, platform, conflictBus]);\n const syncStatusContextValue = useMemo<SyncStatusContextValue>(() => ({\n status: syncStatus,\n pendingMutations,\n pendingCount: pendingMutations.length,\n // Expose uploading/downloading directly from syncStatus for reliable activity detection\n isUploading: syncStatus.uploading,\n isDownloading: syncStatus.downloading,\n isPaused: syncModeState.mode === 'offline',\n syncMode: syncModeState.mode,\n lastSyncedAt,\n // Connection error for consumers to display\n connectionError,\n // Failed transaction fields\n failedTransactions,\n hasUploadErrors: failedTransactions.length > 0,\n permanentErrorCount: failedTransactions.filter(f_0 => f_0.isPermanent).length,\n // Clear failure functions\n clearFailure,\n clearAllFailures,\n // Completed transaction fields\n completedTransactions,\n clearCompletedHistory,\n // Sync mode control functions\n setSyncMode,\n setForceNextUpload,\n // Discard mutation functions\n discardPendingMutation,\n discardAllPendingMutations\n }), [syncStatus, pendingMutations, syncModeState.mode, lastSyncedAt, connectionError, failedTransactions, clearFailure, clearAllFailures, completedTransactions, clearCompletedHistory, setSyncMode, setForceNextUpload, discardPendingMutation, discardAllPendingMutations]);\n const connectionHealthContextValue = useMemo<ConnectionHealthContextValue>(() => ({\n health: connectionHealth\n }), [connectionHealth]);\n const syncMetricsContextValue = useMemo<SyncMetricsContextValue>(() => ({\n metrics: syncMetrics\n }), [syncMetrics]);\n\n // ─── Render ────────────────────────────────────────────────────────────────\n\n return <PowerSyncContext.Provider value={powerSyncContextValue as PowerSyncContextValue}>\n <SyncStatusContext.Provider value={syncStatusContextValue}>\n <ConnectionHealthContext.Provider value={connectionHealthContextValue}>\n <SyncMetricsContext.Provider value={syncMetricsContextValue}>\n <AttachmentQueueContext.Provider value={attachmentQueue}>\n {children}\n </AttachmentQueueContext.Provider>\n </SyncMetricsContext.Provider>\n </ConnectionHealthContext.Provider>\n </SyncStatusContext.Provider>\n </PowerSyncContext.Provider>;\n}","import type { ConflictCheckResult, ConflictResolution } from './types';\nexport type ConflictListener = (conflict: ConflictCheckResult) => void;\nexport type ResolutionListener = (table: string, recordId: string, resolution: ConflictResolution) => void;\n\n/**\n * Event bus for decoupling conflict detection from UI resolution.\n *\n * Flow:\n * 1. Connector detects conflict -> calls bus.emitConflict()\n * 2. ConflictContext subscribes via bus.onConflict() -> shows UI\n * 3. User resolves -> ConflictContext calls bus.emitResolution()\n * 4. Connector subscribes via bus.onResolution() -> triggers re-upload\n */\nexport class ConflictBus {\n private conflictListeners = new Set<ConflictListener>();\n private resolutionListeners = new Set<ResolutionListener>();\n\n /**\n * Subscribe to conflict detection events.\n * @returns Unsubscribe function\n */\n onConflict(listener: ConflictListener): () => void {\n this.conflictListeners.add(listener);\n return () => this.conflictListeners.delete(listener);\n }\n\n /**\n * Subscribe to resolution events.\n * @returns Unsubscribe function\n */\n onResolution(listener: ResolutionListener): () => void {\n this.resolutionListeners.add(listener);\n return () => this.resolutionListeners.delete(listener);\n }\n\n /**\n * Emit a conflict detection event (called by connector).\n */\n emitConflict(conflict: ConflictCheckResult): void {\n this.conflictListeners.forEach(listener => listener(conflict));\n }\n\n /**\n * Emit a resolution event (called by UI/ConflictContext).\n */\n emitResolution(table: string, recordId: string, resolution: ConflictResolution): void {\n this.resolutionListeners.forEach(listener => listener(table, recordId, resolution));\n }\n\n /**\n * Clear all listeners (for cleanup).\n */\n destroy(): void {\n this.conflictListeners.clear();\n this.resolutionListeners.clear();\n }\n}","import { c as _c } from \"react/compiler-runtime\";\n/**\n * React Hooks for @pol-studios/powersync\n *\n * This module provides React hooks for accessing PowerSync functionality\n * within the provider's context.\n */\n\nimport { useContext, useCallback, useMemo, useRef, useState, useEffect } from 'react';\nimport type { AbstractPowerSyncDatabase, SyncStatus, ConnectionHealth, SyncMetrics, CrudEntry, EntitySyncState, FailedTransaction, CompletedTransaction, SyncError } from '../core/types';\nimport type { PlatformAdapter } from '../platform/types';\nimport type { PowerSyncContextValue, SyncStatusContextValue, ConnectionHealthContextValue, SyncMetricsContextValue } from './types';\nimport type { SyncScope, SyncControlActions } from '../sync/types';\nimport type { AttachmentQueue } from '../attachments/attachment-queue';\nimport type { SupabaseConnector } from '../connector/supabase-connector';\nimport { PowerSyncContext, SyncStatusContext, ConnectionHealthContext, SyncMetricsContext, AttachmentQueueContext } from './context';\nimport type { SyncMode } from '../core/types';\n// Note: STORAGE_KEY_PAUSED and STORAGE_KEY_SYNC_MODE are handled by the status tracker\n\n// ─── Main Hook ───────────────────────────────────────────────────────────────\n\n/**\n * Hook to access the PowerSync database and related services.\n *\n * @returns PowerSync context value with database, connector, and state\n * @throws Error if used outside of PowerSyncProvider\n *\n * @example\n * ```typescript\n * function MyComponent() {\n * const { db, isReady, error } = usePowerSync();\n *\n * if (!isReady) return <LoadingSpinner />;\n * if (error) return <Error message={error.message} />;\n *\n * // Use db for queries\n * const users = await db.getAll('SELECT * FROM users');\n * }\n * ```\n */\nexport function usePowerSync() {\n const context = useContext(PowerSyncContext);\n if (!context) {\n throw new Error(\"usePowerSync must be used within a PowerSyncProvider\");\n }\n return context as PowerSyncContextValue<TSchema>;\n}\n\n// ─── Sync Status Hook ────────────────────────────────────────────────────────\n\n/**\n * Hook to access the current sync status.\n *\n * @returns Sync status with connection state, pending uploads, and progress\n * @throws Error if used outside of PowerSyncProvider\n *\n * @example\n * ```typescript\n * function SyncIndicator() {\n * const { status, pendingCount, isPaused } = useSyncStatus();\n *\n * if (status.downloading) {\n * const { current, target, percentage } = status.downloadProgress ?? {};\n * return <Progress value={percentage} label={`${current}/${target}`} />;\n * }\n *\n * if (pendingCount > 0) {\n * return <Badge>{pendingCount} pending uploads</Badge>;\n * }\n *\n * return <Text>Synced</Text>;\n * }\n * ```\n */\nexport function useSyncStatus() {\n const context = useContext(SyncStatusContext);\n if (!context) {\n throw new Error(\"useSyncStatus must be used within a PowerSyncProvider\");\n }\n return context;\n}\n\n// ─── Sync Control Hook ───────────────────────────────────────────────────────\n\n/**\n * Hook to control sync operations.\n *\n * @returns Actions for triggering, pausing, and resuming sync\n *\n * @example\n * ```typescript\n * function SyncControls() {\n * const { triggerSync, syncNow, pause, resume, disconnect, setSyncMode } = useSyncControl();\n * const { isPaused, syncMode } = useSyncStatus();\n *\n * return (\n * <View>\n * <Button onPress={syncNow}>Sync Now</Button>\n * {isPaused ? (\n * <Button onPress={resume}>Resume</Button>\n * ) : (\n * <Button onPress={pause}>Pause</Button>\n * )}\n * </View>\n * );\n * }\n * ```\n */\nexport function useSyncControl() {\n const $ = _c(35);\n const {\n db,\n connector,\n platform\n } = usePowerSync();\n const {\n setSyncMode: setContextSyncMode,\n setForceNextUpload\n } = useSyncStatus();\n const scopeRef = useRef(null);\n let t0;\n if ($[0] !== connector || $[1] !== db || $[2] !== platform || $[3] !== setContextSyncMode) {\n t0 = async mode => {\n await setContextSyncMode(mode);\n if (mode === \"offline\") {\n if (db?.connected) {\n platform.logger.info(\"[useSyncControl] Mode changed to offline - disconnecting\");\n await db.disconnect();\n }\n } else {\n if (db && connector && !db.connected) {\n platform.logger.info(\"[useSyncControl] Mode changed to\", mode, \"- reconnecting\");\n await db.connect(connector);\n }\n }\n };\n $[0] = connector;\n $[1] = db;\n $[2] = platform;\n $[3] = setContextSyncMode;\n $[4] = t0;\n } else {\n t0 = $[4];\n }\n const setSyncMode = t0;\n let t1;\n if ($[5] !== connector || $[6] !== db || $[7] !== platform || $[8] !== setForceNextUpload) {\n t1 = async () => {\n if (!db || !connector) {\n platform.logger.warn(\"[useSyncControl] Cannot sync - database not initialized\");\n return;\n }\n setForceNextUpload(true);\n platform.logger.info(\"[useSyncControl] Sync Now triggered - forcing full sync\");\n if (db.connected) {\n await db.disconnect();\n }\n await db.connect(connector);\n platform.logger.info(\"[useSyncControl] Connected, sync should start automatically\");\n };\n $[5] = connector;\n $[6] = db;\n $[7] = platform;\n $[8] = setForceNextUpload;\n $[9] = t1;\n } else {\n t1 = $[9];\n }\n const syncNow = t1;\n let t2;\n if ($[10] !== connector || $[11] !== db || $[12] !== platform || $[13] !== setContextSyncMode) {\n t2 = async () => {\n if (!db || !connector) {\n platform.logger.warn(\"[useSyncControl] Cannot trigger sync - not initialized\");\n return;\n }\n await setContextSyncMode(\"push-pull\");\n if (db.connected) {\n platform.logger.info(\"[useSyncControl] Disconnecting to force fresh sync...\");\n await db.disconnect();\n }\n platform.logger.info(\"[useSyncControl] Connecting...\");\n await db.connect(connector);\n platform.logger.info(\"[useSyncControl] Connected, sync should start automatically\");\n };\n $[10] = connector;\n $[11] = db;\n $[12] = platform;\n $[13] = setContextSyncMode;\n $[14] = t2;\n } else {\n t2 = $[14];\n }\n const triggerSync = t2;\n let t3;\n if ($[15] !== platform || $[16] !== setSyncMode) {\n t3 = async () => {\n await setSyncMode(\"offline\");\n platform.logger.info(\"[useSyncControl] Sync paused\");\n };\n $[15] = platform;\n $[16] = setSyncMode;\n $[17] = t3;\n } else {\n t3 = $[17];\n }\n const pause = t3;\n let t4;\n if ($[18] !== platform || $[19] !== setSyncMode) {\n t4 = async () => {\n await setSyncMode(\"push-pull\");\n platform.logger.info(\"[useSyncControl] Sync resumed\");\n };\n $[18] = platform;\n $[19] = setSyncMode;\n $[20] = t4;\n } else {\n t4 = $[20];\n }\n const resume = t4;\n let t5;\n if ($[21] !== db || $[22] !== platform) {\n t5 = async () => {\n if (!db) {\n platform.logger.warn(\"[useSyncControl] Cannot disconnect - not initialized\");\n return;\n }\n platform.logger.info(\"[useSyncControl] Disconnecting...\");\n await db.disconnect();\n platform.logger.info(\"[useSyncControl] Disconnected\");\n };\n $[21] = db;\n $[22] = platform;\n $[23] = t5;\n } else {\n t5 = $[23];\n }\n const disconnect = t5;\n let t6;\n if ($[24] !== connector || $[25] !== platform) {\n t6 = scope => {\n scopeRef.current = scope;\n if (connector && scope) {\n connector.setActiveProjectIds(scope.ids);\n platform.logger.info(\"[useSyncControl] Scope set:\", scope);\n }\n };\n $[24] = connector;\n $[25] = platform;\n $[26] = t6;\n } else {\n t6 = $[26];\n }\n const setScope = t6;\n let t7;\n if ($[27] !== disconnect || $[28] !== pause || $[29] !== resume || $[30] !== setScope || $[31] !== setSyncMode || $[32] !== syncNow || $[33] !== triggerSync) {\n t7 = {\n triggerSync,\n syncNow,\n pause,\n resume,\n disconnect,\n setScope,\n setSyncMode\n };\n $[27] = disconnect;\n $[28] = pause;\n $[29] = resume;\n $[30] = setScope;\n $[31] = setSyncMode;\n $[32] = syncNow;\n $[33] = triggerSync;\n $[34] = t7;\n } else {\n t7 = $[34];\n }\n return t7;\n}\n\n// ─── Sync Mode Hook ──────────────────────────────────────────────────────────\n\n/**\n * Hook to get and set the current sync mode.\n *\n * @returns Object with current mode, setter, and capability flags\n *\n * @example\n * ```typescript\n * function SyncModeSelector() {\n * const { mode, setMode, canUpload, canDownload } = useSyncMode();\n *\n * return (\n * <View>\n * <Text>Current mode: {mode}</Text>\n * <Text>Can upload: {canUpload ? 'Yes' : 'No'}</Text>\n * <Button onPress={() => setMode('pull-only')}>Download Only</Button>\n * </View>\n * );\n * }\n * ```\n */\nexport function useSyncMode() {\n const $ = _c(5);\n const {\n syncMode\n } = useSyncStatus();\n const {\n setSyncMode\n } = useSyncControl();\n const t0 = syncMode === \"push-pull\";\n const t1 = syncMode !== \"offline\";\n let t2;\n if ($[0] !== setSyncMode || $[1] !== syncMode || $[2] !== t0 || $[3] !== t1) {\n t2 = {\n mode: syncMode,\n setMode: setSyncMode,\n canUpload: t0,\n canDownload: t1\n };\n $[0] = setSyncMode;\n $[1] = syncMode;\n $[2] = t0;\n $[3] = t1;\n $[4] = t2;\n } else {\n t2 = $[4];\n }\n return t2;\n}\n\n// ─── Connection Health Hook ──────────────────────────────────────────────────\n\n/**\n * Hook to access connection health status.\n *\n * @returns Current connection health with latency and failure tracking\n * @throws Error if used outside of PowerSyncProvider\n *\n * @example\n * ```typescript\n * function ConnectionIndicator() {\n * const health = useConnectionHealth();\n *\n * const statusColor = {\n * healthy: 'green',\n * degraded: 'yellow',\n * disconnected: 'red',\n * }[health.status];\n *\n * return (\n * <View>\n * <StatusDot color={statusColor} />\n * {health.latency && <Text>{health.latency}ms</Text>}\n * </View>\n * );\n * }\n * ```\n */\nexport function useConnectionHealth() {\n const context = useContext(ConnectionHealthContext);\n if (!context) {\n throw new Error(\"useConnectionHealth must be used within a PowerSyncProvider\");\n }\n return context.health;\n}\n\n// ─── Sync Metrics Hook ───────────────────────────────────────────────────────\n\n/**\n * Hook to access sync metrics.\n *\n * @returns Sync metrics including success rates, timing, and data transfer\n * @throws Error if used outside of PowerSyncProvider\n *\n * @example\n * ```typescript\n * function SyncStats() {\n * const metrics = useSyncMetrics();\n *\n * const successRate = metrics.totalSyncs > 0\n * ? (metrics.successfulSyncs / metrics.totalSyncs * 100).toFixed(1)\n * : 100;\n *\n * return (\n * <View>\n * <Text>Total syncs: {metrics.totalSyncs}</Text>\n * <Text>Success rate: {successRate}%</Text>\n * <Text>Avg duration: {metrics.averageSyncDuration ?? 'N/A'}ms</Text>\n * </View>\n * );\n * }\n * ```\n */\nexport function useSyncMetrics() {\n const context = useContext(SyncMetricsContext);\n if (!context) {\n throw new Error(\"useSyncMetrics must be used within a PowerSyncProvider\");\n }\n return context.metrics;\n}\n\n// ─── Attachment Queue Hook ───────────────────────────────────────────────────\n\n/**\n * Hook to access the attachment queue (if configured).\n *\n * @returns AttachmentQueue instance or null if not configured\n *\n * @example\n * ```typescript\n * function PhotoStats() {\n * const attachmentQueue = useAttachmentQueue();\n * const [stats, setStats] = useState(null);\n *\n * useEffect(() => {\n * if (!attachmentQueue) return;\n *\n * return attachmentQueue.onProgress((newStats) => {\n * setStats(newStats);\n * });\n * }, [attachmentQueue]);\n *\n * if (!stats) return null;\n *\n * return (\n * <View>\n * <Text>Photos: {stats.syncedCount}/{stats.totalExpected}</Text>\n * <Text>Cache used: {formatBytes(stats.syncedSize)}</Text>\n * </View>\n * );\n * }\n * ```\n */\nexport function useAttachmentQueue() {\n return useContext(AttachmentQueueContext);\n}\n\n// ─── Database Query Hook ─────────────────────────────────────────────────────\n\n/**\n * Hook to get the PowerSync database instance.\n * Throws if not ready.\n *\n * @returns The PowerSync database instance\n * @throws Error if not initialized or used outside of PowerSyncProvider\n *\n * @example\n * ```typescript\n * function UserList() {\n * const db = useDatabase();\n * const [users, setUsers] = useState([]);\n *\n * useEffect(() => {\n * db.getAll('SELECT * FROM users').then(setUsers);\n * }, [db]);\n *\n * return <FlatList data={users} />;\n * }\n * ```\n */\nexport function useDatabase() {\n const {\n db,\n isReady,\n error\n } = usePowerSync();\n if (error) {\n throw error;\n }\n if (!isReady || !db) {\n throw new Error(\"PowerSync database is not ready\");\n }\n return db;\n}\n\n// ─── Platform Adapter Hook ───────────────────────────────────────────────────\n\n/**\n * Hook to access the platform adapter.\n *\n * @returns The platform adapter instance\n *\n * @example\n * ```typescript\n * function FileViewer({ filePath }) {\n * const { platform } = usePowerSync();\n *\n * const handleOpen = async () => {\n * const content = await platform.fileSystem.readFile(filePath);\n * // Process content...\n * };\n *\n * return <Button onPress={handleOpen}>Open File</Button>;\n * }\n * ```\n */\nexport function usePlatform() {\n const {\n platform\n } = usePowerSync();\n return platform;\n}\n\n// ─── Online Status Hook ──────────────────────────────────────────────────────\n\n/**\n * Hook to track online/offline status using the platform's network adapter.\n *\n * @returns Whether the device is currently connected to the internet\n *\n * @example\n * ```typescript\n * function OfflineBanner() {\n * const isOnline = useOnlineStatus();\n *\n * if (isOnline) return null;\n *\n * return <Banner type=\"warning\">You are offline</Banner>;\n * }\n * ```\n */\nexport function useOnlineStatus() {\n const $ = _c(4);\n const {\n platform\n } = usePowerSync();\n const [isOnline, setIsOnline] = useState(true);\n let t0;\n if ($[0] !== platform.network) {\n t0 = () => {\n platform.network.isConnected().then(setIsOnline);\n const unsubscribe = platform.network.addConnectionListener(setIsOnline);\n return unsubscribe;\n };\n $[0] = platform.network;\n $[1] = t0;\n } else {\n t0 = $[1];\n }\n let t1;\n if ($[2] !== platform) {\n t1 = [platform];\n $[2] = platform;\n $[3] = t1;\n } else {\n t1 = $[3];\n }\n useEffect(t0, t1);\n return isOnline;\n}\n\n// ─── Pending Mutations Hook ──────────────────────────────────────────────────\n\n/**\n * Hook to get pending mutations that need to be uploaded.\n *\n * @returns Array of pending CRUD entries and count\n *\n * @example\n * ```typescript\n * function PendingChanges() {\n * const { mutations, count } = usePendingMutations();\n *\n * if (count === 0) return null;\n *\n * return (\n * <View>\n * <Text>{count} changes pending upload</Text>\n * <FlatList\n * data={mutations}\n * renderItem={({ item }) => (\n * <Text>{item.op} on {item.table}</Text>\n * )}\n * />\n * </View>\n * );\n * }\n * ```\n */\nexport function usePendingMutations() {\n const $ = _c(3);\n const {\n pendingMutations,\n pendingCount\n } = useSyncStatus();\n let t0;\n if ($[0] !== pendingCount || $[1] !== pendingMutations) {\n t0 = {\n mutations: pendingMutations,\n count: pendingCount\n };\n $[0] = pendingCount;\n $[1] = pendingMutations;\n $[2] = t0;\n } else {\n t0 = $[2];\n }\n return t0;\n}\n\n// ─── Is Syncing Hook ─────────────────────────────────────────────────────────\n\n/**\n * Hook to check if sync is currently active.\n *\n * @returns Whether sync is currently in progress (uploading or downloading)\n *\n * @example\n * ```typescript\n * function SyncButton() {\n * const isSyncing = useIsSyncing();\n * const { triggerSync } = useSyncControl();\n *\n * return (\n * <Button\n * onPress={triggerSync}\n * disabled={isSyncing}\n * >\n * {isSyncing ? 'Syncing...' : 'Sync Now'}\n * </Button>\n * );\n * }\n * ```\n */\nexport function useIsSyncing() {\n const {\n status\n } = useSyncStatus();\n return status.uploading || status.downloading;\n}\n\n// ─── Download Progress Hook ──────────────────────────────────────────────────\n\n/**\n * Hook to get download progress during sync.\n *\n * @returns Download progress or null if not downloading\n *\n * @example\n * ```typescript\n * function DownloadProgress() {\n * const progress = useDownloadProgress();\n *\n * if (!progress) return null;\n *\n * return (\n * <ProgressBar\n * value={progress.percentage}\n * label={`${progress.current}/${progress.target} operations`}\n * />\n * );\n * }\n * ```\n */\nexport function useDownloadProgress() {\n const {\n status\n } = useSyncStatus();\n return status.downloadProgress;\n}\n\n// ─── Entity Sync Status Hook ─────────────────────────────────────────────────\n\n/**\n * Return type for useEntitySyncStatus hook.\n */\nexport interface EntitySyncStatusResult {\n /** Current sync state for this entity */\n state: EntitySyncState;\n /** Error details if state is 'error' */\n error: SyncError | null;\n /** Number of pending operations for this entity */\n pendingOperations: number;\n /** The failed transaction if any */\n failedTransaction: FailedTransaction | null;\n /** Dismiss the failure (remove from tracking) */\n dismiss: () => void;\n}\n\n// Track recently synced entities with timestamps\nconst recentlySyncedEntities = new Map<string, number>();\nconst SYNCED_DISPLAY_DURATION_MS = 3000; // Show 'synced' state for 3 seconds\n\n/**\n * Hook to get sync status for a specific entity.\n *\n * Combines local mutation state (from pending mutations) with\n * failure state to provide a unified status for UI.\n *\n * @param entityId - The entity ID to check status for\n * @returns Unified sync state and actions\n *\n * @example\n * ```typescript\n * function EquipmentHeader({ unitId }) {\n * const { state, error, dismiss } = useEntitySyncStatus(unitId);\n *\n * const borderColor = {\n * idle: 'transparent',\n * saving: 'orange',\n * syncing: 'amber',\n * synced: 'green',\n * error: 'red',\n * }[state];\n * }\n * ```\n */\nexport function useEntitySyncStatus(entityId) {\n const $ = _c(27);\n const {\n pendingMutations,\n clearFailure,\n failedTransactions\n } = useSyncStatus();\n const [, forceUpdate] = useState(0);\n let t0;\n bb0: {\n if (!entityId) {\n let t1;\n if ($[0] === Symbol.for(\"react.memo_cache_sentinel\")) {\n t1 = [];\n $[0] = t1;\n } else {\n t1 = $[0];\n }\n t0 = t1;\n break bb0;\n }\n let t1;\n if ($[1] !== entityId || $[2] !== pendingMutations) {\n let t2;\n if ($[4] !== entityId) {\n t2 = entry => entry.id === entityId || String(entry.opData?.id) === entityId;\n $[4] = entityId;\n $[5] = t2;\n } else {\n t2 = $[5];\n }\n t1 = pendingMutations.filter(t2);\n $[1] = entityId;\n $[2] = pendingMutations;\n $[3] = t1;\n } else {\n t1 = $[3];\n }\n t0 = t1;\n }\n const entityPendingMutations = t0;\n let t1;\n bb1: {\n if (!entityId) {\n t1 = null;\n break bb1;\n }\n let t2;\n if ($[6] !== entityId || $[7] !== failedTransactions) {\n t2 = failedTransactions.find(ft => ft.affectedEntityIds.includes(entityId)) ?? null;\n $[6] = entityId;\n $[7] = failedTransactions;\n $[8] = t2;\n } else {\n t2 = $[8];\n }\n t1 = t2;\n }\n const failedTransaction = t1;\n const wasSyncingRef = useRef(false);\n const isCurrentlySyncing = entityPendingMutations.length > 0;\n let t2;\n if ($[9] !== entityId || $[10] !== failedTransaction || $[11] !== forceUpdate || $[12] !== isCurrentlySyncing) {\n t2 = () => {\n if (!entityId) {\n return;\n }\n if (wasSyncingRef.current && !isCurrentlySyncing && !failedTransaction) {\n recentlySyncedEntities.set(entityId, Date.now());\n const timer = setTimeout(() => {\n const syncedAt = recentlySyncedEntities.get(entityId);\n if (syncedAt && Date.now() - syncedAt >= SYNCED_DISPLAY_DURATION_MS) {\n recentlySyncedEntities.delete(entityId);\n forceUpdate(_temp);\n }\n }, SYNCED_DISPLAY_DURATION_MS);\n return () => clearTimeout(timer);\n }\n wasSyncingRef.current = isCurrentlySyncing;\n };\n $[9] = entityId;\n $[10] = failedTransaction;\n $[11] = forceUpdate;\n $[12] = isCurrentlySyncing;\n $[13] = t2;\n } else {\n t2 = $[13];\n }\n let t3;\n if ($[14] !== entityId || $[15] !== failedTransaction || $[16] !== isCurrentlySyncing) {\n t3 = [entityId, isCurrentlySyncing, failedTransaction];\n $[14] = entityId;\n $[15] = failedTransaction;\n $[16] = isCurrentlySyncing;\n $[17] = t3;\n } else {\n t3 = $[17];\n }\n useEffect(t2, t3);\n let t4;\n bb2: {\n if (!entityId) {\n t4 = \"idle\";\n break bb2;\n }\n if (failedTransaction) {\n t4 = \"error\";\n break bb2;\n }\n if (entityPendingMutations.length > 0) {\n t4 = \"syncing\";\n break bb2;\n }\n const syncedAt_0 = recentlySyncedEntities.get(entityId);\n if (syncedAt_0 && Date.now() - syncedAt_0 < SYNCED_DISPLAY_DURATION_MS) {\n t4 = \"synced\";\n break bb2;\n }\n t4 = \"idle\";\n }\n const state = t4;\n const error = failedTransaction?.error ?? null;\n let t5;\n if ($[18] !== clearFailure || $[19] !== failedTransaction) {\n t5 = () => {\n if (failedTransaction) {\n clearFailure(failedTransaction.id);\n }\n };\n $[18] = clearFailure;\n $[19] = failedTransaction;\n $[20] = t5;\n } else {\n t5 = $[20];\n }\n const dismiss = t5;\n let t6;\n if ($[21] !== dismiss || $[22] !== entityPendingMutations.length || $[23] !== error || $[24] !== failedTransaction || $[25] !== state) {\n t6 = {\n state,\n error,\n pendingOperations: entityPendingMutations.length,\n failedTransaction,\n dismiss\n };\n $[21] = dismiss;\n $[22] = entityPendingMutations.length;\n $[23] = error;\n $[24] = failedTransaction;\n $[25] = state;\n $[26] = t6;\n } else {\n t6 = $[26];\n }\n return t6;\n}\n\n// ─── Upload Status Hook ──────────────────────────────────────────────────────\n\n/**\n * Return type for useUploadStatus hook.\n */\nfunction _temp(n) {\n return n + 1;\n}\nexport interface UploadStatusResult {\n /** Number of operations waiting to upload */\n pendingCount: number;\n /** Number of failed transactions */\n failedCount: number;\n /** Number of permanent failures needing user action */\n permanentFailureCount: number;\n /** Whether there are any errors */\n hasErrors: boolean;\n /** Whether there are permanent errors needing attention */\n hasPermanentErrors: boolean;\n /** All failed transactions */\n failedTransactions: FailedTransaction[];\n /** Trigger sync retry (reconnect) */\n retryAll: () => Promise<void>;\n /** Dismiss all failures */\n dismissAll: () => void;\n}\n\n/**\n * Hook to get overall upload status across all entities.\n *\n * @returns Upload status with counts and actions\n *\n * @example\n * ```typescript\n * function SyncStatusBar() {\n * const { pendingCount, failedCount, hasPermanentErrors, retryAll } = useUploadStatus();\n *\n * if (hasPermanentErrors) {\n * return <Banner onRetry={retryAll}>\n * {failedCount} changes failed to sync\n * </Banner>;\n * }\n * }\n * ```\n */\nexport function useUploadStatus() {\n const $ = _c(12);\n const {\n pendingCount,\n clearAllFailures,\n failedTransactions,\n hasUploadErrors,\n permanentErrorCount\n } = useSyncStatus();\n const {\n triggerSync\n } = useSyncControl();\n let t0;\n if ($[0] !== triggerSync) {\n t0 = async () => {\n await triggerSync();\n };\n $[0] = triggerSync;\n $[1] = t0;\n } else {\n t0 = $[1];\n }\n const retryAll = t0;\n let t1;\n if ($[2] !== clearAllFailures) {\n t1 = () => {\n clearAllFailures();\n };\n $[2] = clearAllFailures;\n $[3] = t1;\n } else {\n t1 = $[3];\n }\n const dismissAll = t1;\n const t2 = permanentErrorCount > 0;\n let t3;\n if ($[4] !== dismissAll || $[5] !== failedTransactions || $[6] !== hasUploadErrors || $[7] !== pendingCount || $[8] !== permanentErrorCount || $[9] !== retryAll || $[10] !== t2) {\n t3 = {\n pendingCount,\n failedCount: failedTransactions.length,\n permanentFailureCount: permanentErrorCount,\n hasErrors: hasUploadErrors,\n hasPermanentErrors: t2,\n failedTransactions,\n retryAll,\n dismissAll\n };\n $[4] = dismissAll;\n $[5] = failedTransactions;\n $[6] = hasUploadErrors;\n $[7] = pendingCount;\n $[8] = permanentErrorCount;\n $[9] = retryAll;\n $[10] = t2;\n $[11] = t3;\n } else {\n t3 = $[11];\n }\n return t3;\n}\n\n// ─── Sync Activity Hook ──────────────────────────────────────────────────────\n\n/**\n * Return type for useSyncActivity hook.\n */\nexport interface SyncActivityResult {\n /** Pending CRUD entries waiting to be synced */\n pending: CrudEntry[];\n /** Failed transactions that need attention */\n failed: FailedTransaction[];\n /** Recently completed transactions */\n completed: CompletedTransaction[];\n /** Counts summary */\n counts: {\n pending: number;\n failed: number;\n completed: number;\n };\n /** Whether there is any sync activity to show (pending or failed) */\n hasActivity: boolean;\n /** Retry all failed transactions */\n retryAll: () => Promise<void>;\n /** Dismiss a specific failure */\n dismissFailure: (failureId: string) => void;\n /** Clear all completed transactions from the list */\n clearCompleted: () => void;\n}\n\n/**\n * Hook to get comprehensive sync activity including pending, failed, and completed transactions.\n *\n * Uses the provider's completed transaction tracking via the status tracker.\n *\n * @returns Sync activity with all transaction states and actions\n *\n * @example\n * ```typescript\n * function SyncActivityBanner() {\n * const { pending, failed, completed, counts, hasActivity, retryAll, clearCompleted } = useSyncActivity();\n *\n * if (!hasActivity && completed.length === 0) return null;\n *\n * return (\n * <View>\n * <Text>{counts.pending} syncing, {counts.failed} failed</Text>\n * {failed.length > 0 && <Button onPress={retryAll}>Retry All</Button>}\n * </View>\n * );\n * }\n * ```\n */\nexport function useSyncActivity() {\n const $ = _c(19);\n const {\n pendingMutations,\n clearFailure,\n failedTransactions,\n completedTransactions,\n clearCompletedHistory,\n isUploading,\n isDownloading\n } = useSyncStatus();\n const {\n triggerSync\n } = useSyncControl();\n let t0;\n if ($[0] !== triggerSync) {\n t0 = async () => {\n await triggerSync();\n };\n $[0] = triggerSync;\n $[1] = t0;\n } else {\n t0 = $[1];\n }\n const retryAll = t0;\n let t1;\n if ($[2] !== clearFailure) {\n t1 = failureId => {\n clearFailure(failureId);\n };\n $[2] = clearFailure;\n $[3] = t1;\n } else {\n t1 = $[3];\n }\n const dismissFailure = t1;\n let t2;\n if ($[4] !== clearCompletedHistory) {\n t2 = () => {\n clearCompletedHistory();\n };\n $[4] = clearCompletedHistory;\n $[5] = t2;\n } else {\n t2 = $[5];\n }\n const clearCompleted = t2;\n let t3;\n if ($[6] !== completedTransactions.length || $[7] !== failedTransactions.length || $[8] !== pendingMutations.length) {\n t3 = {\n pending: pendingMutations.length,\n failed: failedTransactions.length,\n completed: completedTransactions.length\n };\n $[6] = completedTransactions.length;\n $[7] = failedTransactions.length;\n $[8] = pendingMutations.length;\n $[9] = t3;\n } else {\n t3 = $[9];\n }\n const counts = t3;\n const hasActivity = isUploading || isDownloading || failedTransactions.length > 0;\n let t4;\n if ($[10] !== clearCompleted || $[11] !== completedTransactions || $[12] !== counts || $[13] !== dismissFailure || $[14] !== failedTransactions || $[15] !== hasActivity || $[16] !== pendingMutations || $[17] !== retryAll) {\n t4 = {\n pending: pendingMutations,\n failed: failedTransactions,\n completed: completedTransactions,\n counts,\n hasActivity,\n retryAll,\n dismissFailure,\n clearCompleted\n };\n $[10] = clearCompleted;\n $[11] = completedTransactions;\n $[12] = counts;\n $[13] = dismissFailure;\n $[14] = failedTransactions;\n $[15] = hasActivity;\n $[16] = pendingMutations;\n $[17] = retryAll;\n $[18] = t4;\n } else {\n t4 = $[18];\n }\n return t4;\n}"],"mappings":"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","names":["useRef","useState","useEffect","useRef","useState","useEffect","t1","t2"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=chunk-OLHGI472.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -7,11 +7,11 @@ import {
|
|
|
7
7
|
STORAGE_KEY_METRICS,
|
|
8
8
|
STORAGE_KEY_PAUSED,
|
|
9
9
|
STORAGE_KEY_SYNC_MODE
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-EJ23MXPQ.js";
|
|
11
11
|
import {
|
|
12
12
|
classifyError,
|
|
13
13
|
generateFailureId
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-FPTDATY5.js";
|
|
15
15
|
|
|
16
16
|
// src/provider/types.ts
|
|
17
17
|
var DEFAULT_SYNC_STATUS = {
|
|
@@ -82,7 +82,9 @@ var SyncStatusTracker = class _SyncStatusTracker {
|
|
|
82
82
|
this.notifyThrottleMs = options.notifyThrottleMs ?? STATUS_NOTIFY_THROTTLE_MS;
|
|
83
83
|
this.onStatusChange = options.onStatusChange;
|
|
84
84
|
this._state = {
|
|
85
|
-
status: {
|
|
85
|
+
status: {
|
|
86
|
+
...DEFAULT_SYNC_STATUS
|
|
87
|
+
},
|
|
86
88
|
syncMode: "push-pull",
|
|
87
89
|
lastUpdated: /* @__PURE__ */ new Date()
|
|
88
90
|
};
|
|
@@ -369,9 +371,7 @@ var SyncStatusTracker = class _SyncStatusTracker {
|
|
|
369
371
|
};
|
|
370
372
|
this._failedTransactions.push(newFailure);
|
|
371
373
|
if (this._failedTransactions.length > this._maxStoredFailures) {
|
|
372
|
-
this._failedTransactions.sort(
|
|
373
|
-
(a, b) => a.firstFailedAt.getTime() - b.firstFailedAt.getTime()
|
|
374
|
-
);
|
|
374
|
+
this._failedTransactions.sort((a, b) => a.firstFailedAt.getTime() - b.firstFailedAt.getTime());
|
|
375
375
|
this._failedTransactions = this._failedTransactions.slice(-this._maxStoredFailures);
|
|
376
376
|
}
|
|
377
377
|
}
|
|
@@ -402,9 +402,7 @@ var SyncStatusTracker = class _SyncStatusTracker {
|
|
|
402
402
|
* Get failures affecting a specific entity.
|
|
403
403
|
*/
|
|
404
404
|
getFailuresForEntity(entityId) {
|
|
405
|
-
return this._failedTransactions.filter(
|
|
406
|
-
(f) => f.affectedEntityIds.includes(entityId)
|
|
407
|
-
);
|
|
405
|
+
return this._failedTransactions.filter((f) => f.affectedEntityIds.includes(entityId));
|
|
408
406
|
}
|
|
409
407
|
/**
|
|
410
408
|
* Get all failed transactions.
|
|
@@ -441,13 +439,9 @@ var SyncStatusTracker = class _SyncStatusTracker {
|
|
|
441
439
|
cleanupStaleFailures() {
|
|
442
440
|
const cutoff = Date.now() - this._failureTTLMs;
|
|
443
441
|
const initialLength = this._failedTransactions.length;
|
|
444
|
-
this._failedTransactions = this._failedTransactions.filter(
|
|
445
|
-
(f) => f.lastFailedAt.getTime() > cutoff
|
|
446
|
-
);
|
|
442
|
+
this._failedTransactions = this._failedTransactions.filter((f) => f.lastFailedAt.getTime() > cutoff);
|
|
447
443
|
if (this._failedTransactions.length !== initialLength) {
|
|
448
|
-
this.logger.debug(
|
|
449
|
-
`[StatusTracker] Cleaned up ${initialLength - this._failedTransactions.length} stale failures`
|
|
450
|
-
);
|
|
444
|
+
this.logger.debug(`[StatusTracker] Cleaned up ${initialLength - this._failedTransactions.length} stale failures`);
|
|
451
445
|
this._notifyFailureListeners();
|
|
452
446
|
this._notifyListeners();
|
|
453
447
|
}
|
|
@@ -473,9 +467,7 @@ var SyncStatusTracker = class _SyncStatusTracker {
|
|
|
473
467
|
this._completedTransactions = this._completedTransactions.slice(0, this._maxCompletedHistory);
|
|
474
468
|
}
|
|
475
469
|
this._notifyCompletedListeners();
|
|
476
|
-
this.logger.debug(
|
|
477
|
-
`[StatusTracker] Recorded completed transaction: ${completed.id} (${entries.length} entries)`
|
|
478
|
-
);
|
|
470
|
+
this.logger.debug(`[StatusTracker] Recorded completed transaction: ${completed.id} (${entries.length} entries)`);
|
|
479
471
|
}
|
|
480
472
|
/**
|
|
481
473
|
* Get all completed transactions.
|
|
@@ -575,7 +567,9 @@ var MetricsCollector = class {
|
|
|
575
567
|
this.storageKey = options.storageKey ?? STORAGE_KEY_METRICS;
|
|
576
568
|
this.persistMetrics = options.persistMetrics ?? true;
|
|
577
569
|
this.onMetricsChange = options.onMetricsChange;
|
|
578
|
-
this._metrics = {
|
|
570
|
+
this._metrics = {
|
|
571
|
+
...DEFAULT_SYNC_METRICS
|
|
572
|
+
};
|
|
579
573
|
}
|
|
580
574
|
// ─── Initialization ────────────────────────────────────────────────────────
|
|
581
575
|
/**
|
|
@@ -590,7 +584,10 @@ var MetricsCollector = class {
|
|
|
590
584
|
if (parsed.lastError?.timestamp) {
|
|
591
585
|
parsed.lastError.timestamp = new Date(parsed.lastError.timestamp);
|
|
592
586
|
}
|
|
593
|
-
this._metrics = {
|
|
587
|
+
this._metrics = {
|
|
588
|
+
...DEFAULT_SYNC_METRICS,
|
|
589
|
+
...parsed
|
|
590
|
+
};
|
|
594
591
|
this.logger.debug("[MetricsCollector] Loaded persisted metrics");
|
|
595
592
|
}
|
|
596
593
|
} catch (err) {
|
|
@@ -609,14 +606,22 @@ var MetricsCollector = class {
|
|
|
609
606
|
* Get current sync metrics.
|
|
610
607
|
*/
|
|
611
608
|
getMetrics() {
|
|
612
|
-
return {
|
|
609
|
+
return {
|
|
610
|
+
...this._metrics
|
|
611
|
+
};
|
|
613
612
|
}
|
|
614
613
|
// ─── Recording ─────────────────────────────────────────────────────────────
|
|
615
614
|
/**
|
|
616
615
|
* Record a completed sync operation.
|
|
617
616
|
*/
|
|
618
617
|
async recordSync(data) {
|
|
619
|
-
const {
|
|
618
|
+
const {
|
|
619
|
+
durationMs,
|
|
620
|
+
success,
|
|
621
|
+
operationsDownloaded,
|
|
622
|
+
operationsUploaded,
|
|
623
|
+
error
|
|
624
|
+
} = data;
|
|
620
625
|
const totalSyncs = this._metrics.totalSyncs + 1;
|
|
621
626
|
const successfulSyncs = this._metrics.successfulSyncs + (success ? 1 : 0);
|
|
622
627
|
const failedSyncs = this._metrics.failedSyncs + (success ? 0 : 1);
|
|
@@ -691,7 +696,9 @@ var MetricsCollector = class {
|
|
|
691
696
|
* Clear all metrics and start fresh.
|
|
692
697
|
*/
|
|
693
698
|
async reset() {
|
|
694
|
-
this._metrics = {
|
|
699
|
+
this._metrics = {
|
|
700
|
+
...DEFAULT_SYNC_METRICS
|
|
701
|
+
};
|
|
695
702
|
await this._persist();
|
|
696
703
|
this._notifyListeners();
|
|
697
704
|
}
|
|
@@ -779,7 +786,9 @@ var HealthMonitor = class {
|
|
|
779
786
|
this.degradedThresholdMs = options.degradedThresholdMs ?? LATENCY_DEGRADED_THRESHOLD_MS;
|
|
780
787
|
this.maxConsecutiveFailures = options.maxConsecutiveFailures ?? MAX_CONSECUTIVE_FAILURES;
|
|
781
788
|
this.onHealthChange = options.onHealthChange;
|
|
782
|
-
this._health = {
|
|
789
|
+
this._health = {
|
|
790
|
+
...DEFAULT_CONNECTION_HEALTH
|
|
791
|
+
};
|
|
783
792
|
}
|
|
784
793
|
// ─── Lifecycle ─────────────────────────────────────────────────────────────
|
|
785
794
|
/**
|
|
@@ -852,7 +861,9 @@ var HealthMonitor = class {
|
|
|
852
861
|
* Get current connection health.
|
|
853
862
|
*/
|
|
854
863
|
getHealth() {
|
|
855
|
-
return {
|
|
864
|
+
return {
|
|
865
|
+
...this._health
|
|
866
|
+
};
|
|
856
867
|
}
|
|
857
868
|
/**
|
|
858
869
|
* Check if the monitor is running.
|
|
@@ -912,10 +923,7 @@ var HealthMonitor = class {
|
|
|
912
923
|
const startTime = Date.now();
|
|
913
924
|
const timestamp = /* @__PURE__ */ new Date();
|
|
914
925
|
try {
|
|
915
|
-
await this._withTimeout(
|
|
916
|
-
this._db.get("SELECT 1"),
|
|
917
|
-
this.checkTimeoutMs
|
|
918
|
-
);
|
|
926
|
+
await this._withTimeout(this._db.get("SELECT 1"), this.checkTimeoutMs);
|
|
919
927
|
const latencyMs = Date.now() - startTime;
|
|
920
928
|
const status = latencyMs < this.degradedThresholdMs ? "healthy" : "degraded";
|
|
921
929
|
this._updateHealth({
|
|
@@ -976,16 +984,13 @@ var HealthMonitor = class {
|
|
|
976
984
|
const timer = setTimeout(() => {
|
|
977
985
|
reject(new Error(`Health check timeout after ${timeoutMs}ms`));
|
|
978
986
|
}, timeoutMs);
|
|
979
|
-
promise.then(
|
|
980
|
-
(
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
(error)
|
|
985
|
-
|
|
986
|
-
reject(error);
|
|
987
|
-
}
|
|
988
|
-
);
|
|
987
|
+
promise.then((result) => {
|
|
988
|
+
clearTimeout(timer);
|
|
989
|
+
resolve(result);
|
|
990
|
+
}, (error) => {
|
|
991
|
+
clearTimeout(timer);
|
|
992
|
+
reject(error);
|
|
993
|
+
});
|
|
989
994
|
});
|
|
990
995
|
}
|
|
991
996
|
};
|
|
@@ -999,4 +1004,4 @@ export {
|
|
|
999
1004
|
MetricsCollector,
|
|
1000
1005
|
HealthMonitor
|
|
1001
1006
|
};
|
|
1002
|
-
//# sourceMappingURL=chunk-
|
|
1007
|
+
//# sourceMappingURL=chunk-OTJXIRWX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/provider/types.ts","../src/sync/status-tracker.ts","../src/sync/metrics-collector.ts","../src/sync/health-monitor.ts"],"sourcesContent":["/**\n * Provider Types for @pol-studios/powersync\n *\n * Defines configuration and context interfaces for the PowerSyncProvider.\n */\n\nimport type { SupabaseClient } from '@supabase/supabase-js';\nimport type { QueryClient } from '@tanstack/react-query';\nimport type { AbstractPowerSyncDatabase, SyncStatus, ConnectionHealth, SyncMetrics, CrudEntry, FailedTransaction, CompletedTransaction, SyncMode } from '../core/types';\nimport type { PlatformAdapter } from '../platform/types';\nimport type { ConnectorConfig } from '../connector/types';\nimport type { AttachmentQueueConfig } from '../attachments/types';\nimport type { AttachmentQueue } from '../attachments/attachment-queue';\nimport type { SupabaseConnector } from '../connector/supabase-connector';\nimport type { ConflictBus } from '../conflicts/conflict-bus';\n\n// ─── Provider Configuration ──────────────────────────────────────────────────\n\n/**\n * Main configuration for PowerSyncProvider.\n *\n * @template TSchema - The PowerSync schema type\n *\n * @example\n * ```typescript\n * const config: PowerSyncConfig<AppSchema> = {\n * platform: createNativePlatformAdapter(logger),\n * schema: AppSchema,\n * powerSyncUrl: 'https://your-powersync-instance.com',\n * supabaseClient: supabase,\n * dbFilename: 'myapp.db',\n * sync: {\n * autoConnect: true,\n * },\n * };\n * ```\n */\nexport interface PowerSyncConfig<TSchema = unknown> {\n /**\n * Platform adapter for platform-specific operations.\n * Use createNativePlatformAdapter() for React Native or createWebPlatformAdapter() for Web.\n */\n platform: PlatformAdapter;\n\n /**\n * PowerSync schema definition.\n * This defines the tables and their structure for the local database.\n */\n schema: TSchema;\n\n /**\n * PowerSync service URL.\n * @example \"https://your-instance.powersync.journeyapps.com\"\n */\n powerSyncUrl: string;\n\n /**\n * Supabase client instance.\n * Used for authentication and as the backend for CRUD uploads.\n */\n supabaseClient: SupabaseClient;\n\n /**\n * Optional: TanStack Query client for cache invalidation.\n * If provided, will invalidate queries when sync completes.\n */\n queryClient?: QueryClient;\n\n /**\n * Optional: Database filename.\n * @default \"powersync.db\"\n */\n dbFilename?: string;\n\n /**\n * Optional: Connector configuration for custom CRUD handling.\n */\n connector?: ConnectorConfig;\n\n /**\n * Optional: Attachment queue configuration for offline file caching.\n */\n attachments?: AttachmentQueueConfig;\n\n /**\n * Optional: Sync behavior configuration.\n */\n sync?: SyncConfig;\n}\n\n/**\n * Sync behavior configuration.\n */\nexport interface SyncConfig {\n /**\n * Automatically connect when the provider mounts and there's an authenticated session.\n * @default true\n */\n autoConnect?: boolean;\n\n /**\n * Sync interval for periodic sync checks (in milliseconds).\n * Set to 0 to disable periodic sync.\n * @default 0 (disabled)\n */\n syncInterval?: number;\n\n /**\n * Enable health monitoring.\n * @default true\n */\n enableHealthMonitoring?: boolean;\n\n /**\n * Enable metrics collection.\n * @default true\n */\n enableMetrics?: boolean;\n}\n\n// ─── Context Types ───────────────────────────────────────────────────────────\n\n/**\n * Value provided by the main PowerSyncContext.\n *\n * @template TSchema - The PowerSync schema type\n */\nexport interface PowerSyncContextValue<TSchema = unknown> {\n /**\n * The PowerSync database instance.\n * Will be null if not initialized or if initialization failed.\n */\n db: AbstractPowerSyncDatabase | null;\n\n /**\n * The Supabase connector instance.\n * Will be null if not initialized.\n */\n connector: SupabaseConnector | null;\n\n /**\n * The attachment queue instance.\n * Will be null if attachments are not configured or not initialized.\n */\n attachmentQueue: AttachmentQueue | null;\n\n /**\n * Whether the PowerSync database is ready for use.\n */\n isReady: boolean;\n\n /**\n * Whether the provider is currently initializing.\n */\n isInitializing: boolean;\n\n /**\n * Error that occurred during initialization, if any.\n */\n error: Error | null;\n\n /**\n * The schema used for this database.\n */\n schema: TSchema;\n\n /**\n * The platform adapter instance.\n */\n platform: PlatformAdapter;\n\n /**\n * The conflict bus for subscribing to conflict events.\n * Use this to wire up conflict UI components.\n */\n conflictBus: ConflictBus;\n}\n\n/**\n * Value provided by SyncStatusContext.\n */\nexport interface SyncStatusContextValue {\n /**\n * Current sync status.\n */\n status: SyncStatus;\n\n /**\n * Pending mutations waiting to be uploaded.\n */\n pendingMutations: CrudEntry[];\n\n /**\n * Number of pending mutations.\n */\n pendingCount: number;\n\n /**\n * Whether data is currently being uploaded to the server.\n * This is the authoritative source for upload activity.\n */\n isUploading: boolean;\n\n /**\n * Whether data is currently being downloaded from the server.\n * This is the authoritative source for download activity.\n */\n isDownloading: boolean;\n\n /**\n * Whether sync is currently paused (offline mode).\n */\n isPaused: boolean;\n\n /**\n * Current sync mode: 'push-pull' (full sync), 'pull-only' (download only), or 'offline' (no sync).\n */\n syncMode: SyncMode;\n\n /**\n * Timestamp of the last successful sync.\n */\n lastSyncedAt: Date | null;\n\n /**\n * Error that occurred during connection, if any.\n */\n connectionError?: Error | null;\n\n /**\n * Failed transactions that need attention.\n */\n failedTransactions: FailedTransaction[];\n\n /**\n * Whether there are any upload errors.\n */\n hasUploadErrors: boolean;\n\n /**\n * Count of permanent errors that need user action.\n */\n permanentErrorCount: number;\n\n /**\n * Clear a specific failure by its ID.\n */\n clearFailure: (failureId: string) => void;\n\n /**\n * Clear all failures.\n */\n clearAllFailures: () => void;\n\n /**\n * Completed transactions history.\n */\n completedTransactions: CompletedTransaction[];\n\n /**\n * Clear the completed transaction history.\n */\n clearCompletedHistory: () => void;\n\n /**\n * Set the sync mode.\n * @param mode - The sync mode to set\n */\n setSyncMode: (mode: SyncMode) => Promise<void>;\n\n /**\n * Set the force next upload flag.\n * When true, the next sync cycle will upload regardless of sync mode.\n */\n setForceNextUpload: (force: boolean) => void;\n\n /**\n * Discard a specific pending mutation by its client ID.\n * Uses safe disconnect/reconnect pattern to avoid transaction conflicts.\n * @throws Error if upload is in progress\n */\n discardPendingMutation: (clientId: number) => Promise<void>;\n\n /**\n * Discard all pending mutations.\n * Uses safe disconnect/reconnect pattern to avoid transaction conflicts.\n * @throws Error if upload is in progress\n */\n discardAllPendingMutations: () => Promise<void>;\n}\n\n/**\n * Value provided by ConnectionHealthContext.\n */\nexport interface ConnectionHealthContextValue {\n /**\n * Current connection health status.\n */\n health: ConnectionHealth;\n}\n\n/**\n * Value provided by SyncMetricsContext.\n */\nexport interface SyncMetricsContextValue {\n /**\n * Current sync metrics.\n */\n metrics: SyncMetrics;\n}\n\n// ─── Provider Props ──────────────────────────────────────────────────────────\n\n/**\n * Props for the PowerSyncProvider component.\n *\n * @template TSchema - The PowerSync schema type\n */\nexport interface PowerSyncProviderProps<TSchema = unknown> {\n /**\n * PowerSync configuration.\n */\n config: PowerSyncConfig<TSchema>;\n\n /**\n * Child components to render.\n */\n children: React.ReactNode;\n\n /**\n * Called when the database is initialized and ready.\n */\n onReady?: () => void;\n\n /**\n * Called when an error occurs during initialization.\n */\n onError?: (error: Error) => void;\n\n /**\n * Called when sync status changes.\n */\n onSyncStatusChange?: (status: SyncStatus) => void;\n}\n\n// ─── Default Values ──────────────────────────────────────────────────────────\n\n/**\n * Default sync status.\n */\nexport const DEFAULT_SYNC_STATUS: SyncStatus = {\n connected: false,\n connecting: false,\n hasSynced: false,\n lastSyncedAt: null,\n uploading: false,\n downloading: false,\n downloadProgress: null,\n failedTransactions: [],\n hasUploadErrors: false,\n permanentErrorCount: 0\n};\n\n/**\n * Default connection health.\n */\nexport const DEFAULT_CONNECTION_HEALTH: ConnectionHealth = {\n status: 'disconnected',\n latency: null,\n lastHealthCheck: null,\n consecutiveFailures: 0,\n reconnectAttempts: 0\n};\n\n/**\n * Default sync metrics.\n */\nexport const DEFAULT_SYNC_METRICS: SyncMetrics = {\n totalSyncs: 0,\n successfulSyncs: 0,\n failedSyncs: 0,\n lastSyncDuration: null,\n averageSyncDuration: null,\n totalDataDownloaded: 0,\n totalDataUploaded: 0,\n lastError: null\n};\n\n/**\n * Default sync configuration.\n */\nexport const DEFAULT_SYNC_CONFIG: Required<SyncConfig> = {\n autoConnect: true,\n syncInterval: 0,\n enableHealthMonitoring: true,\n enableMetrics: true\n};","/**\n * Sync Status Tracker for @pol-studios/powersync\n *\n * Tracks and normalizes PowerSync status changes, providing a consistent\n * interface for status updates with throttling support.\n */\n\nimport type { SyncStatus, DownloadProgress, CrudEntry, FailedTransaction, SyncError, CompletedTransaction, SyncMode } from '../core/types';\nimport { generateFailureId } from '../core/errors';\nimport type { LoggerAdapter, AsyncStorageAdapter } from '../platform/types';\nimport type { SyncStatusState, SyncStatusTrackerOptions, PowerSyncRawStatus, Unsubscribe } from './types';\nimport { STORAGE_KEY_PAUSED, STORAGE_KEY_SYNC_MODE, STATUS_NOTIFY_THROTTLE_MS } from '../core/constants';\nimport { DEFAULT_SYNC_STATUS } from '../provider/types';\n\n/**\n * Tracks sync status from PowerSync and provides normalized updates.\n *\n * Features:\n * - Normalizes raw PowerSync status to a consistent format\n * - Throttles notifications to prevent UI thrashing\n * - Tracks pending mutations count\n * - Persists and restores paused state\n *\n * @example\n * ```typescript\n * const tracker = new SyncStatusTracker({\n * storage,\n * logger,\n * onStatusChange: (status) => console.log('Status:', status),\n * });\n *\n * // Register with PowerSync\n * db.registerListener({\n * statusChanged: (rawStatus) => tracker.handleStatusChange(rawStatus),\n * });\n *\n * // Get current status\n * const status = tracker.getStatus();\n * ```\n */\nexport class SyncStatusTracker {\n private readonly storage: AsyncStorageAdapter;\n private readonly logger: LoggerAdapter;\n private readonly notifyThrottleMs: number;\n private readonly onStatusChange?: (status: SyncStatus) => void;\n private _state: SyncStatusState;\n private _pendingMutations: CrudEntry[] = [];\n private _lastNotifyTime = 0;\n private _notifyTimer: ReturnType<typeof setTimeout> | null = null;\n private _listeners = new Set<(status: SyncStatus) => void>();\n private _syncModeListeners = new Set<(mode: SyncMode) => void>();\n\n // Force next upload flag for \"Sync Now\" functionality\n private _forceNextUpload = false;\n\n // Track download progress separately to preserve it when offline\n private _lastProgress: DownloadProgress | null = null;\n\n // Failed transaction tracking\n private _failedTransactions: FailedTransaction[] = [];\n private readonly _maxStoredFailures = 50;\n private readonly _failureTTLMs = 24 * 60 * 60 * 1000; // 24 hours\n private _failureListeners = new Set<(failures: FailedTransaction[]) => void>();\n\n // Completed transaction tracking\n private _completedTransactions: CompletedTransaction[] = [];\n private readonly _maxCompletedHistory = 20;\n private _completedListeners = new Set<(completed: CompletedTransaction[]) => void>();\n constructor(storage: AsyncStorageAdapter, logger: LoggerAdapter, options: SyncStatusTrackerOptions = {}) {\n this.storage = storage;\n this.logger = logger;\n this.notifyThrottleMs = options.notifyThrottleMs ?? STATUS_NOTIFY_THROTTLE_MS;\n this.onStatusChange = options.onStatusChange;\n this._state = {\n status: {\n ...DEFAULT_SYNC_STATUS\n },\n syncMode: 'push-pull',\n lastUpdated: new Date()\n };\n }\n\n // ─── Initialization ────────────────────────────────────────────────────────\n\n /**\n * Initialize the tracker by loading persisted state.\n * Includes migration from old isPaused boolean to new syncMode.\n */\n async init(): Promise<void> {\n try {\n // Try to load new sync mode first\n const modeValue = await this.storage.getItem(STORAGE_KEY_SYNC_MODE);\n if (modeValue && ['push-pull', 'pull-only', 'offline'].includes(modeValue)) {\n this._state.syncMode = modeValue as SyncMode;\n this.logger.debug('[StatusTracker] Loaded sync mode:', this._state.syncMode);\n } else {\n // Migrate from old isPaused boolean\n const pausedValue = await this.storage.getItem(STORAGE_KEY_PAUSED);\n if (pausedValue === 'true') {\n this._state.syncMode = 'offline';\n await this.storage.setItem(STORAGE_KEY_SYNC_MODE, 'offline');\n this.logger.debug('[StatusTracker] Migrated isPaused=true to syncMode=offline');\n } else {\n this._state.syncMode = 'push-pull';\n }\n // Clean up old key\n await this.storage.removeItem(STORAGE_KEY_PAUSED);\n }\n } catch (err) {\n this.logger.warn('[StatusTracker] Failed to load sync mode:', err);\n }\n }\n\n /**\n * Dispose the tracker and clear timers.\n */\n dispose(): void {\n if (this._notifyTimer) {\n clearTimeout(this._notifyTimer);\n this._notifyTimer = null;\n }\n this._listeners.clear();\n this._syncModeListeners.clear();\n this._failureListeners.clear();\n this._completedListeners.clear();\n }\n\n // ─── Status Getters ────────────────────────────────────────────────────────\n\n /**\n * Get the current sync status.\n */\n getStatus(): SyncStatus {\n const baseStatus = this._state.status;\n\n // Build the status with failed transaction info\n const status: SyncStatus = {\n ...baseStatus,\n failedTransactions: this._failedTransactions,\n hasUploadErrors: this._failedTransactions.length > 0,\n permanentErrorCount: this._failedTransactions.filter(f => f.isPermanent).length\n };\n\n // If offline, use saved progress instead of live (which would be null)\n if (this._state.syncMode === 'offline' && this._lastProgress) {\n return {\n ...status,\n downloadProgress: this._lastProgress\n };\n }\n return status;\n }\n\n // ─── Sync Mode Getters ────────────────────────────────────────────────────────\n\n /**\n * Get the current sync mode.\n */\n getSyncMode(): SyncMode {\n return this._state.syncMode;\n }\n\n /**\n * Get whether sync is paused (offline mode).\n * @deprecated Use getSyncMode() instead\n */\n isPaused(): boolean {\n return this._state.syncMode === 'offline';\n }\n\n /**\n * Check if uploads are allowed based on current sync mode.\n */\n canUpload(): boolean {\n return this._state.syncMode === 'push-pull';\n }\n\n /**\n * Check if downloads are allowed based on current sync mode.\n */\n canDownload(): boolean {\n return this._state.syncMode !== 'offline';\n }\n\n /**\n * Set the force next upload flag for \"Sync Now\" functionality.\n */\n setForceNextUpload(force: boolean): void {\n this._forceNextUpload = force;\n this.logger.debug('[StatusTracker] Force next upload set to:', force);\n }\n\n /**\n * Clear the force next upload flag.\n * Should be called after all pending uploads have been processed.\n */\n clearForceNextUpload(): void {\n if (this._forceNextUpload) {\n this._forceNextUpload = false;\n this.logger.debug('[StatusTracker] Force next upload flag cleared');\n }\n }\n\n /**\n * Check if upload should proceed, considering force flag.\n * NOTE: Does NOT auto-reset the flag - caller must use clearForceNextUpload()\n * after all uploads are complete. This prevents race conditions when\n * PowerSync calls uploadData() multiple times for multiple transactions.\n */\n shouldUpload(): boolean {\n // Return true if force flag is set OR if mode is push-pull\n if (this._forceNextUpload) {\n return true;\n }\n return this._state.syncMode === 'push-pull';\n }\n\n /**\n * Get pending mutations.\n */\n getPendingMutations(): CrudEntry[] {\n return this._pendingMutations;\n }\n\n /**\n * Get pending mutation count.\n */\n getPendingCount(): number {\n return this._pendingMutations.length;\n }\n\n // ─── Status Updates ────────────────────────────────────────────────────────\n\n /**\n * Handle a raw status update from PowerSync.\n */\n handleStatusChange(rawStatus: PowerSyncRawStatus): void {\n const progress = rawStatus.downloadProgress;\n const dataFlow = rawStatus.dataFlowStatus;\n\n // Build normalized download progress\n let downloadProgress: DownloadProgress | null = null;\n if (progress && progress.totalOperations && progress.totalOperations > 0) {\n downloadProgress = {\n current: progress.downloadedOperations ?? 0,\n target: progress.totalOperations,\n percentage: Math.round((progress.downloadedFraction ?? 0) * 100)\n };\n // Save progress for when paused\n this._lastProgress = downloadProgress;\n }\n\n // Build normalized status (failed transaction fields are added in getStatus())\n const newStatus: SyncStatus = {\n connected: rawStatus.connected ?? false,\n connecting: rawStatus.connecting ?? false,\n hasSynced: rawStatus.hasSynced ?? false,\n lastSyncedAt: rawStatus.lastSyncedAt ?? null,\n uploading: dataFlow?.uploading ?? false,\n downloading: dataFlow?.downloading ?? false,\n downloadProgress,\n // These are computed from _failedTransactions in getStatus()\n failedTransactions: [],\n hasUploadErrors: false,\n permanentErrorCount: 0\n };\n\n // Check if status actually changed\n const changed = this._hasStatusChanged(newStatus);\n this._state = {\n status: newStatus,\n syncMode: this._state.syncMode,\n lastUpdated: new Date()\n };\n if (changed) {\n this._notifyListeners();\n }\n }\n\n /**\n * Update pending mutations from a CRUD transaction.\n */\n updatePendingMutations(mutations: CrudEntry[]): void {\n this._pendingMutations = mutations;\n }\n\n /**\n * Valid sync modes for runtime validation.\n */\n private static readonly VALID_SYNC_MODES: SyncMode[] = ['push-pull', 'pull-only', 'offline'];\n\n /**\n * Set the sync mode.\n */\n async setSyncMode(mode: SyncMode): Promise<void> {\n // Runtime validation\n if (!SyncStatusTracker.VALID_SYNC_MODES.includes(mode)) {\n this.logger.warn('[StatusTracker] Invalid sync mode, ignoring:', mode);\n return;\n }\n if (this._state.syncMode === mode) return;\n const previousMode = this._state.syncMode;\n this._state.syncMode = mode;\n\n // Persist to storage\n try {\n await this.storage.setItem(STORAGE_KEY_SYNC_MODE, mode);\n this.logger.info('[StatusTracker] Sync mode changed:', previousMode, '->', mode);\n } catch (err) {\n this.logger.warn('[StatusTracker] Failed to persist sync mode:', err);\n }\n\n // Notify sync mode listeners\n for (const listener of this._syncModeListeners) {\n try {\n listener(mode);\n } catch (err) {\n this.logger.warn('[StatusTracker] Sync mode listener error:', err);\n }\n }\n this._notifyListeners(true);\n }\n\n /**\n * Set paused state.\n * @deprecated Use setSyncMode() instead\n */\n async setPaused(paused: boolean): Promise<void> {\n await this.setSyncMode(paused ? 'offline' : 'push-pull');\n }\n\n // ─── Subscriptions ─────────────────────────────────────────────────────────\n\n /**\n * Subscribe to status changes.\n * @returns Unsubscribe function\n */\n onStatusUpdate(listener: (status: SyncStatus) => void): Unsubscribe {\n this._listeners.add(listener);\n // Immediately call with current status\n listener(this.getStatus());\n return () => {\n this._listeners.delete(listener);\n };\n }\n\n /**\n * Subscribe to sync mode changes.\n * @returns Unsubscribe function\n */\n onSyncModeChange(listener: (mode: SyncMode) => void): Unsubscribe {\n this._syncModeListeners.add(listener);\n listener(this._state.syncMode);\n return () => {\n this._syncModeListeners.delete(listener);\n };\n }\n\n /**\n * Subscribe to paused state changes.\n * @deprecated Use onSyncModeChange() instead\n * @returns Unsubscribe function\n */\n onPausedChange(listener: (isPaused: boolean) => void): Unsubscribe {\n // Wrap the listener to convert SyncMode to isPaused boolean\n const wrappedListener = (mode: SyncMode) => {\n listener(mode === 'offline');\n };\n this._syncModeListeners.add(wrappedListener);\n listener(this._state.syncMode === 'offline');\n return () => {\n this._syncModeListeners.delete(wrappedListener);\n };\n }\n\n // ─── Failed Transaction Tracking ────────────────────────────────────────────\n\n /**\n * Record a transaction failure.\n * If a failure for the same entries already exists, updates the retry count.\n * Otherwise, creates a new failure record.\n */\n recordTransactionFailure(entries: CrudEntry[], error: SyncError, isPermanent: boolean, affectedEntityIds: string[], affectedTables: string[]): void {\n const now = new Date();\n const entryIds = entries.map(e => e.id).sort().join(',');\n\n // Check if a failure for these entries already exists\n const existingIndex = this._failedTransactions.findIndex(f => {\n const existingIds = f.entries.map(e => e.id).sort().join(',');\n return existingIds === entryIds;\n });\n if (existingIndex !== -1) {\n // Update existing failure\n const existing = this._failedTransactions[existingIndex];\n this._failedTransactions[existingIndex] = {\n ...existing,\n error,\n retryCount: existing.retryCount + 1,\n lastFailedAt: now,\n isPermanent\n };\n } else {\n // Create new failure record\n const newFailure: FailedTransaction = {\n id: generateFailureId(entries),\n entries,\n error,\n retryCount: 1,\n firstFailedAt: now,\n lastFailedAt: now,\n isPermanent,\n affectedEntityIds,\n affectedTables\n };\n this._failedTransactions.push(newFailure);\n\n // Enforce max stored failures (remove oldest)\n if (this._failedTransactions.length > this._maxStoredFailures) {\n this._failedTransactions.sort((a, b) => a.firstFailedAt.getTime() - b.firstFailedAt.getTime());\n this._failedTransactions = this._failedTransactions.slice(-this._maxStoredFailures);\n }\n }\n this._notifyFailureListeners();\n this._notifyListeners();\n }\n\n /**\n * Clear a specific failure by ID.\n */\n clearFailure(failureId: string): void {\n const initialLength = this._failedTransactions.length;\n this._failedTransactions = this._failedTransactions.filter(f => f.id !== failureId);\n if (this._failedTransactions.length !== initialLength) {\n this._notifyFailureListeners();\n this._notifyListeners();\n }\n }\n\n /**\n * Clear all failures.\n */\n clearAllFailures(): void {\n if (this._failedTransactions.length === 0) return;\n this._failedTransactions = [];\n this._notifyFailureListeners();\n this._notifyListeners();\n }\n\n /**\n * Get failures affecting a specific entity.\n */\n getFailuresForEntity(entityId: string): FailedTransaction[] {\n return this._failedTransactions.filter(f => f.affectedEntityIds.includes(entityId));\n }\n\n /**\n * Get all failed transactions.\n */\n getFailedTransactions(): FailedTransaction[] {\n return [...this._failedTransactions];\n }\n\n /**\n * Check if there are any upload errors.\n */\n hasUploadErrors(): boolean {\n return this._failedTransactions.length > 0;\n }\n\n /**\n * Get count of permanent errors.\n */\n getPermanentErrorCount(): number {\n return this._failedTransactions.filter(f => f.isPermanent).length;\n }\n\n /**\n * Subscribe to failure changes.\n * @returns Unsubscribe function\n */\n onFailureChange(listener: (failures: FailedTransaction[]) => void): Unsubscribe {\n this._failureListeners.add(listener);\n // Immediately call with current failures\n listener(this.getFailedTransactions());\n return () => {\n this._failureListeners.delete(listener);\n };\n }\n\n /**\n * Clean up stale failures (older than TTL).\n */\n cleanupStaleFailures(): void {\n const cutoff = Date.now() - this._failureTTLMs;\n const initialLength = this._failedTransactions.length;\n this._failedTransactions = this._failedTransactions.filter(f => f.lastFailedAt.getTime() > cutoff);\n if (this._failedTransactions.length !== initialLength) {\n this.logger.debug(`[StatusTracker] Cleaned up ${initialLength - this._failedTransactions.length} stale failures`);\n this._notifyFailureListeners();\n this._notifyListeners();\n }\n }\n\n // ─── Completed Transaction Tracking ────────────────────────────────────────\n\n /**\n * Record a successfully completed transaction.\n * Creates a CompletedTransaction record and adds it to history.\n */\n recordTransactionComplete(entries: CrudEntry[]): void {\n // Extract unique affected tables\n const affectedTables = [...new Set(entries.map(e => e.table))];\n\n // Extract unique affected entity IDs\n const affectedEntityIds = [...new Set(entries.map(e => e.id))];\n\n // Generate unique ID\n const id = `completed_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;\n const completed: CompletedTransaction = {\n id,\n entries,\n completedAt: new Date(),\n affectedTables,\n affectedEntityIds\n };\n\n // Add to front of array (most recent first)\n this._completedTransactions.unshift(completed);\n\n // Trim to max history\n if (this._completedTransactions.length > this._maxCompletedHistory) {\n this._completedTransactions = this._completedTransactions.slice(0, this._maxCompletedHistory);\n }\n this._notifyCompletedListeners();\n this.logger.debug(`[StatusTracker] Recorded completed transaction: ${completed.id} (${entries.length} entries)`);\n }\n\n /**\n * Get all completed transactions.\n */\n getCompletedTransactions(): CompletedTransaction[] {\n return [...this._completedTransactions];\n }\n\n /**\n * Clear completed transaction history.\n */\n clearCompletedHistory(): void {\n if (this._completedTransactions.length === 0) return;\n this._completedTransactions = [];\n this._notifyCompletedListeners();\n this.logger.debug('[StatusTracker] Cleared completed transaction history');\n }\n\n /**\n * Subscribe to completed transaction changes.\n * @returns Unsubscribe function\n */\n onCompletedChange(listener: (completed: CompletedTransaction[]) => void): Unsubscribe {\n this._completedListeners.add(listener);\n // Immediately call with current completed transactions\n listener(this.getCompletedTransactions());\n return () => {\n this._completedListeners.delete(listener);\n };\n }\n\n // ─── Private Methods ───────────────────────────────────────────────────────\n\n private _hasStatusChanged(newStatus: SyncStatus): boolean {\n const old = this._state.status;\n 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;\n }\n private _notifyListeners(forceImmediate = false): void {\n const now = Date.now();\n const timeSinceLastNotify = now - this._lastNotifyTime;\n if (this._notifyTimer) {\n clearTimeout(this._notifyTimer);\n this._notifyTimer = null;\n }\n const notify = () => {\n this._lastNotifyTime = Date.now();\n const status = this.getStatus();\n\n // Call the main callback\n this.onStatusChange?.(status);\n\n // Call all listeners\n for (const listener of this._listeners) {\n try {\n listener(status);\n } catch (err) {\n this.logger.warn('[StatusTracker] Listener error:', err);\n }\n }\n };\n if (forceImmediate || timeSinceLastNotify >= this.notifyThrottleMs) {\n notify();\n } else {\n const delay = this.notifyThrottleMs - timeSinceLastNotify;\n this._notifyTimer = setTimeout(notify, delay);\n }\n }\n private _notifyFailureListeners(): void {\n const failures = this.getFailedTransactions();\n for (const listener of this._failureListeners) {\n try {\n listener(failures);\n } catch (err) {\n this.logger.warn('[StatusTracker] Failure listener error:', err);\n }\n }\n }\n private _notifyCompletedListeners(): void {\n const completed = this.getCompletedTransactions();\n for (const listener of this._completedListeners) {\n try {\n listener(completed);\n } catch (err) {\n this.logger.warn('[StatusTracker] Completed listener error:', err);\n }\n }\n }\n}","/**\n * Sync Metrics Collector for @pol-studios/powersync\n *\n * Collects and persists sync operation metrics for monitoring and debugging.\n */\n\nimport type { SyncMetrics, SyncError, SyncErrorType } from '../core/types';\nimport type { AsyncStorageAdapter, LoggerAdapter } from '../platform/types';\nimport type { MetricsCollectorOptions, SyncOperationData, Unsubscribe } from './types';\nimport { STORAGE_KEY_METRICS } from '../core/constants';\nimport { classifyError } from '../core/errors';\nimport { DEFAULT_SYNC_METRICS } from '../provider/types';\n\n/**\n * Collects sync metrics including operation counts, timing, and errors.\n *\n * Features:\n * - Tracks sync operation success/failure rates\n * - Calculates average sync duration\n * - Monitors data transfer amounts\n * - Persists metrics to storage for continuity across sessions\n * - Records last error for debugging\n *\n * @example\n * ```typescript\n * const collector = new MetricsCollector({\n * storage,\n * logger,\n * onMetricsChange: (metrics) => updateUI(metrics),\n * });\n *\n * // Start tracking a sync\n * const startTime = Date.now();\n *\n * // On sync complete\n * collector.recordSync({\n * durationMs: Date.now() - startTime,\n * success: true,\n * operationsDownloaded: 150,\n * });\n *\n * // Get current metrics\n * const metrics = collector.getMetrics();\n * ```\n */\nexport class MetricsCollector {\n private readonly storage: AsyncStorageAdapter;\n private readonly logger: LoggerAdapter;\n private readonly storageKey: string;\n private readonly persistMetrics: boolean;\n private readonly onMetricsChange?: (metrics: SyncMetrics) => void;\n private _metrics: SyncMetrics;\n private _listeners = new Set<(metrics: SyncMetrics) => void>();\n private _initialized = false;\n\n // Track active sync for timing\n private _syncStartTime: number | null = null;\n private _wasSyncing = false;\n constructor(storage: AsyncStorageAdapter, logger: LoggerAdapter, options: MetricsCollectorOptions = {}) {\n this.storage = storage;\n this.logger = logger;\n this.storageKey = options.storageKey ?? STORAGE_KEY_METRICS;\n this.persistMetrics = options.persistMetrics ?? true;\n this.onMetricsChange = options.onMetricsChange;\n this._metrics = {\n ...DEFAULT_SYNC_METRICS\n };\n }\n\n // ─── Initialization ────────────────────────────────────────────────────────\n\n /**\n * Initialize the collector by loading persisted metrics.\n */\n async init(): Promise<void> {\n if (this._initialized) return;\n try {\n const stored = await this.storage.getItem(this.storageKey);\n if (stored) {\n const parsed = JSON.parse(stored);\n // Restore Date objects\n if (parsed.lastError?.timestamp) {\n parsed.lastError.timestamp = new Date(parsed.lastError.timestamp);\n }\n this._metrics = {\n ...DEFAULT_SYNC_METRICS,\n ...parsed\n };\n this.logger.debug('[MetricsCollector] Loaded persisted metrics');\n }\n } catch (err) {\n this.logger.warn('[MetricsCollector] Failed to load metrics:', err);\n }\n this._initialized = true;\n }\n\n /**\n * Dispose the collector.\n */\n dispose(): void {\n this._listeners.clear();\n }\n\n // ─── Getters ───────────────────────────────────────────────────────────────\n\n /**\n * Get current sync metrics.\n */\n getMetrics(): SyncMetrics {\n return {\n ...this._metrics\n };\n }\n\n // ─── Recording ─────────────────────────────────────────────────────────────\n\n /**\n * Record a completed sync operation.\n */\n async recordSync(data: SyncOperationData): Promise<void> {\n const {\n durationMs,\n success,\n operationsDownloaded,\n operationsUploaded,\n error\n } = data;\n const totalSyncs = this._metrics.totalSyncs + 1;\n const successfulSyncs = this._metrics.successfulSyncs + (success ? 1 : 0);\n const failedSyncs = this._metrics.failedSyncs + (success ? 0 : 1);\n\n // Calculate running average duration (only for successful syncs)\n let averageSyncDuration = this._metrics.averageSyncDuration;\n if (success) {\n if (averageSyncDuration !== null) {\n averageSyncDuration = (averageSyncDuration * (successfulSyncs - 1) + durationMs) / successfulSyncs;\n } else {\n averageSyncDuration = durationMs;\n }\n }\n\n // Estimate data transfer (rough approximation: ~100 bytes per operation)\n const bytesPerOp = 100;\n const downloaded = (operationsDownloaded ?? 0) * bytesPerOp;\n const uploaded = (operationsUploaded ?? 0) * bytesPerOp;\n\n // Build error record if failed\n let lastError: SyncError | null = this._metrics.lastError;\n if (!success && error) {\n const errorType = classifyError(error);\n lastError = {\n type: errorType,\n message: error.message,\n userMessage: error.message,\n // Use original message as user message for metrics\n timestamp: new Date(),\n isPermanent: false\n };\n }\n this._metrics = {\n totalSyncs,\n successfulSyncs,\n failedSyncs,\n lastSyncDuration: durationMs,\n averageSyncDuration: averageSyncDuration !== null ? Math.round(averageSyncDuration) : null,\n totalDataDownloaded: this._metrics.totalDataDownloaded + downloaded,\n totalDataUploaded: this._metrics.totalDataUploaded + uploaded,\n lastError\n };\n await this._persist();\n this._notifyListeners();\n }\n\n /**\n * Record a sync error without a full sync operation.\n */\n async recordError(error: Error): Promise<void> {\n this._metrics = {\n ...this._metrics,\n failedSyncs: this._metrics.failedSyncs + 1,\n lastError: {\n type: classifyError(error),\n message: error.message,\n userMessage: error.message,\n // Use original message as user message for metrics\n timestamp: new Date(),\n isPermanent: false\n }\n };\n await this._persist();\n this._notifyListeners();\n }\n\n /**\n * Record an upload operation.\n */\n async recordUpload(operationCount: number): Promise<void> {\n const bytesPerOp = 100;\n this._metrics = {\n ...this._metrics,\n totalDataUploaded: this._metrics.totalDataUploaded + operationCount * bytesPerOp\n };\n await this._persist();\n this._notifyListeners();\n }\n\n /**\n * Clear all metrics and start fresh.\n */\n async reset(): Promise<void> {\n this._metrics = {\n ...DEFAULT_SYNC_METRICS\n };\n await this._persist();\n this._notifyListeners();\n }\n\n // ─── Sync Timing Helpers ───────────────────────────────────────────────────\n\n /**\n * Called when sync starts (downloading becomes true).\n */\n markSyncStart(): void {\n if (!this._wasSyncing) {\n this._syncStartTime = Date.now();\n this._wasSyncing = true;\n }\n }\n\n /**\n * Called when sync ends (downloading becomes false).\n * Returns the duration if a sync was in progress.\n */\n markSyncEnd(): number | null {\n if (this._wasSyncing && this._syncStartTime !== null) {\n const duration = Date.now() - this._syncStartTime;\n this._syncStartTime = null;\n this._wasSyncing = false;\n return duration;\n }\n return null;\n }\n\n /**\n * Check if sync is currently in progress.\n */\n isSyncInProgress(): boolean {\n return this._wasSyncing;\n }\n\n // ─── Subscriptions ─────────────────────────────────────────────────────────\n\n /**\n * Subscribe to metrics changes.\n * @returns Unsubscribe function\n */\n onMetricsUpdate(listener: (metrics: SyncMetrics) => void): Unsubscribe {\n this._listeners.add(listener);\n listener(this.getMetrics());\n return () => {\n this._listeners.delete(listener);\n };\n }\n\n // ─── Private Methods ───────────────────────────────────────────────────────\n\n private async _persist(): Promise<void> {\n if (!this.persistMetrics) return;\n try {\n await this.storage.setItem(this.storageKey, JSON.stringify(this._metrics));\n } catch (err) {\n this.logger.warn('[MetricsCollector] Failed to persist metrics:', err);\n }\n }\n private _notifyListeners(): void {\n const metrics = this.getMetrics();\n\n // Call main callback\n this.onMetricsChange?.(metrics);\n\n // Call all listeners\n for (const listener of this._listeners) {\n try {\n listener(metrics);\n } catch (err) {\n this.logger.warn('[MetricsCollector] Listener error:', err);\n }\n }\n }\n}","/**\n * Connection Health Monitor for @pol-studios/powersync\n *\n * Monitors database connection health with periodic checks and latency tracking.\n */\n\nimport type { ConnectionHealth } from '../core/types';\nimport type { AbstractPowerSyncDatabase } from '../core/types';\nimport type { LoggerAdapter } from '../platform/types';\nimport type { HealthMonitorOptions, HealthCheckResult, Unsubscribe } from './types';\nimport { HEALTH_CHECK_INTERVAL_MS, HEALTH_CHECK_TIMEOUT_MS, LATENCY_DEGRADED_THRESHOLD_MS, MAX_CONSECUTIVE_FAILURES } from '../core/constants';\nimport { DEFAULT_CONNECTION_HEALTH } from '../provider/types';\n\n/**\n * Monitors connection health with periodic checks.\n *\n * Features:\n * - Periodic health checks with configurable interval\n * - Latency measurement and degraded state detection\n * - Consecutive failure tracking\n * - Auto-recovery detection\n *\n * @example\n * ```typescript\n * const monitor = new HealthMonitor(db, logger, {\n * checkIntervalMs: 30000,\n * onHealthChange: (health) => {\n * if (health.status === 'degraded') {\n * showWarning('Connection is slow');\n * }\n * },\n * });\n *\n * // Start monitoring\n * monitor.start();\n *\n * // Get current health\n * const health = monitor.getHealth();\n *\n * // Stop when done\n * monitor.stop();\n * ```\n */\nexport class HealthMonitor {\n private readonly logger: LoggerAdapter;\n private readonly checkIntervalMs: number;\n private readonly checkTimeoutMs: number;\n private readonly degradedThresholdMs: number;\n private readonly maxConsecutiveFailures: number;\n private readonly onHealthChange?: (health: ConnectionHealth) => void;\n private _db: AbstractPowerSyncDatabase | null = null;\n private _health: ConnectionHealth;\n private _intervalId: ReturnType<typeof setInterval> | null = null;\n private _listeners = new Set<(health: ConnectionHealth) => void>();\n private _running = false;\n private _paused = false;\n constructor(logger: LoggerAdapter, options: HealthMonitorOptions = {}) {\n this.logger = logger;\n this.checkIntervalMs = options.checkIntervalMs ?? HEALTH_CHECK_INTERVAL_MS;\n this.checkTimeoutMs = options.checkTimeoutMs ?? HEALTH_CHECK_TIMEOUT_MS;\n this.degradedThresholdMs = options.degradedThresholdMs ?? LATENCY_DEGRADED_THRESHOLD_MS;\n this.maxConsecutiveFailures = options.maxConsecutiveFailures ?? MAX_CONSECUTIVE_FAILURES;\n this.onHealthChange = options.onHealthChange;\n this._health = {\n ...DEFAULT_CONNECTION_HEALTH\n };\n }\n\n // ─── Lifecycle ─────────────────────────────────────────────────────────────\n\n /**\n * Set the database instance to monitor.\n */\n setDatabase(db: AbstractPowerSyncDatabase | null): void {\n this._db = db;\n if (!db) {\n // Reset health when database is cleared\n this._updateHealth({\n status: 'disconnected',\n latency: null,\n lastHealthCheck: new Date(),\n consecutiveFailures: 0,\n reconnectAttempts: this._health.reconnectAttempts\n });\n }\n }\n\n /**\n * Start the health monitor.\n */\n start(): void {\n if (this._running) return;\n this.logger.info('[HealthMonitor] Starting');\n this._running = true;\n\n // Perform initial check\n this._checkHealth();\n\n // Set up periodic checks\n this._intervalId = setInterval(() => {\n if (!this._paused) {\n this._checkHealth();\n }\n }, this.checkIntervalMs);\n }\n\n /**\n * Stop the health monitor.\n */\n stop(): void {\n if (!this._running) return;\n this.logger.info('[HealthMonitor] Stopping');\n this._running = false;\n if (this._intervalId) {\n clearInterval(this._intervalId);\n this._intervalId = null;\n }\n }\n\n /**\n * Pause health checks temporarily.\n */\n pause(): void {\n this._paused = true;\n this._updateHealth({\n ...this._health,\n status: 'disconnected'\n });\n }\n\n /**\n * Resume health checks.\n */\n resume(): void {\n this._paused = false;\n // Perform immediate check on resume\n this._checkHealth();\n }\n\n /**\n * Dispose the monitor and clear all resources.\n */\n dispose(): void {\n this.stop();\n this._listeners.clear();\n }\n\n // ─── Getters ───────────────────────────────────────────────────────────────\n\n /**\n * Get current connection health.\n */\n getHealth(): ConnectionHealth {\n return {\n ...this._health\n };\n }\n\n /**\n * Check if the monitor is running.\n */\n isRunning(): boolean {\n return this._running;\n }\n\n // ─── Manual Checks ─────────────────────────────────────────────────────────\n\n /**\n * Perform an immediate health check.\n * @returns The result of the health check\n */\n async checkNow(): Promise<HealthCheckResult> {\n return this._checkHealth();\n }\n\n /**\n * Record a reconnection attempt.\n */\n recordReconnectAttempt(): void {\n this._updateHealth({\n ...this._health,\n reconnectAttempts: this._health.reconnectAttempts + 1\n });\n }\n\n /**\n * Reset reconnection attempts counter (call on successful connection).\n */\n resetReconnectAttempts(): void {\n if (this._health.reconnectAttempts > 0) {\n this._updateHealth({\n ...this._health,\n reconnectAttempts: 0\n });\n }\n }\n\n // ─── Subscriptions ─────────────────────────────────────────────────────────\n\n /**\n * Subscribe to health changes.\n * @returns Unsubscribe function\n */\n onHealthUpdate(listener: (health: ConnectionHealth) => void): Unsubscribe {\n this._listeners.add(listener);\n listener(this.getHealth());\n return () => {\n this._listeners.delete(listener);\n };\n }\n\n // ─── Private Methods ───────────────────────────────────────────────────────\n\n private async _checkHealth(): Promise<HealthCheckResult> {\n if (!this._db || this._paused) {\n return {\n success: false,\n error: new Error('Database not available or paused'),\n timestamp: new Date()\n };\n }\n const startTime = Date.now();\n const timestamp = new Date();\n try {\n // Execute a simple query with timeout\n await this._withTimeout(this._db.get('SELECT 1'), this.checkTimeoutMs);\n const latencyMs = Date.now() - startTime;\n\n // Determine status based on latency\n const status: ConnectionHealth['status'] = latencyMs < this.degradedThresholdMs ? 'healthy' : 'degraded';\n this._updateHealth({\n status,\n latency: latencyMs,\n lastHealthCheck: timestamp,\n consecutiveFailures: 0,\n reconnectAttempts: this._health.reconnectAttempts\n });\n return {\n success: true,\n latencyMs,\n timestamp\n };\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.logger.warn('[HealthMonitor] Health check failed:', error.message);\n const consecutiveFailures = this._health.consecutiveFailures + 1;\n const status: ConnectionHealth['status'] = consecutiveFailures >= this.maxConsecutiveFailures ? 'disconnected' : 'degraded';\n this._updateHealth({\n status,\n latency: null,\n lastHealthCheck: timestamp,\n consecutiveFailures,\n reconnectAttempts: this._health.reconnectAttempts\n });\n return {\n success: false,\n error,\n timestamp\n };\n }\n }\n private _updateHealth(health: ConnectionHealth): void {\n const changed = this._hasHealthChanged(health);\n this._health = health;\n if (changed) {\n this._notifyListeners();\n }\n }\n private _hasHealthChanged(newHealth: ConnectionHealth): boolean {\n const old = this._health;\n return old.status !== newHealth.status || old.latency !== newHealth.latency || old.consecutiveFailures !== newHealth.consecutiveFailures || old.reconnectAttempts !== newHealth.reconnectAttempts;\n }\n private _notifyListeners(): void {\n const health = this.getHealth();\n\n // Call main callback\n this.onHealthChange?.(health);\n\n // Call all listeners\n for (const listener of this._listeners) {\n try {\n listener(health);\n } catch (err) {\n this.logger.warn('[HealthMonitor] Listener error:', err);\n }\n }\n }\n private _withTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n reject(new Error(`Health check timeout after ${timeoutMs}ms`));\n }, timeoutMs);\n promise.then(result => {\n clearTimeout(timer);\n resolve(result);\n }, error => {\n clearTimeout(timer);\n reject(error);\n });\n });\n }\n}"],"mappings":";;;;;;;;;;;;;;;;AA8VO,IAAM,sBAAkC;AAAA,EAC7C,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,cAAc;AAAA,EACd,WAAW;AAAA,EACX,aAAa;AAAA,EACb,kBAAkB;AAAA,EAClB,oBAAoB,CAAC;AAAA,EACrB,iBAAiB;AAAA,EACjB,qBAAqB;AACvB;AAKO,IAAM,4BAA8C;AAAA,EACzD,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,iBAAiB;AAAA,EACjB,qBAAqB;AAAA,EACrB,mBAAmB;AACrB;AAKO,IAAM,uBAAoC;AAAA,EAC/C,YAAY;AAAA,EACZ,iBAAiB;AAAA,EACjB,aAAa;AAAA,EACb,kBAAkB;AAAA,EAClB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,mBAAmB;AAAA,EACnB,WAAW;AACb;AAKO,IAAM,sBAA4C;AAAA,EACvD,aAAa;AAAA,EACb,cAAc;AAAA,EACd,wBAAwB;AAAA,EACxB,eAAe;AACjB;;;ACpWO,IAAM,oBAAN,MAAM,mBAAkB;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACT;AAAA,EACA,oBAAiC,CAAC;AAAA,EAClC,kBAAkB;AAAA,EAClB,eAAqD;AAAA,EACrD,aAAa,oBAAI,IAAkC;AAAA,EACnD,qBAAqB,oBAAI,IAA8B;AAAA;AAAA,EAGvD,mBAAmB;AAAA;AAAA,EAGnB,gBAAyC;AAAA;AAAA,EAGzC,sBAA2C,CAAC;AAAA,EACnC,qBAAqB;AAAA,EACrB,gBAAgB,KAAK,KAAK,KAAK;AAAA;AAAA,EACxC,oBAAoB,oBAAI,IAA6C;AAAA;AAAA,EAGrE,yBAAiD,CAAC;AAAA,EACzC,uBAAuB;AAAA,EAChC,sBAAsB,oBAAI,IAAiD;AAAA,EACnF,YAAY,SAA8B,QAAuB,UAAoC,CAAC,GAAG;AACvG,SAAK,UAAU;AACf,SAAK,SAAS;AACd,SAAK,mBAAmB,QAAQ,oBAAoB;AACpD,SAAK,iBAAiB,QAAQ;AAC9B,SAAK,SAAS;AAAA,MACZ,QAAQ;AAAA,QACN,GAAG;AAAA,MACL;AAAA,MACA,UAAU;AAAA,MACV,aAAa,oBAAI,KAAK;AAAA,IACxB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,OAAsB;AAC1B,QAAI;AAEF,YAAM,YAAY,MAAM,KAAK,QAAQ,QAAQ,qBAAqB;AAClE,UAAI,aAAa,CAAC,aAAa,aAAa,SAAS,EAAE,SAAS,SAAS,GAAG;AAC1E,aAAK,OAAO,WAAW;AACvB,aAAK,OAAO,MAAM,qCAAqC,KAAK,OAAO,QAAQ;AAAA,MAC7E,OAAO;AAEL,cAAM,cAAc,MAAM,KAAK,QAAQ,QAAQ,kBAAkB;AACjE,YAAI,gBAAgB,QAAQ;AAC1B,eAAK,OAAO,WAAW;AACvB,gBAAM,KAAK,QAAQ,QAAQ,uBAAuB,SAAS;AAC3D,eAAK,OAAO,MAAM,4DAA4D;AAAA,QAChF,OAAO;AACL,eAAK,OAAO,WAAW;AAAA,QACzB;AAEA,cAAM,KAAK,QAAQ,WAAW,kBAAkB;AAAA,MAClD;AAAA,IACF,SAAS,KAAK;AACZ,WAAK,OAAO,KAAK,6CAA6C,GAAG;AAAA,IACnE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,UAAgB;AACd,QAAI,KAAK,cAAc;AACrB,mBAAa,KAAK,YAAY;AAC9B,WAAK,eAAe;AAAA,IACtB;AACA,SAAK,WAAW,MAAM;AACtB,SAAK,mBAAmB,MAAM;AAC9B,SAAK,kBAAkB,MAAM;AAC7B,SAAK,oBAAoB,MAAM;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,YAAwB;AACtB,UAAM,aAAa,KAAK,OAAO;AAG/B,UAAM,SAAqB;AAAA,MACzB,GAAG;AAAA,MACH,oBAAoB,KAAK;AAAA,MACzB,iBAAiB,KAAK,oBAAoB,SAAS;AAAA,MACnD,qBAAqB,KAAK,oBAAoB,OAAO,OAAK,EAAE,WAAW,EAAE;AAAA,IAC3E;AAGA,QAAI,KAAK,OAAO,aAAa,aAAa,KAAK,eAAe;AAC5D,aAAO;AAAA,QACL,GAAG;AAAA,QACH,kBAAkB,KAAK;AAAA,MACzB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,cAAwB;AACtB,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,WAAoB;AAClB,WAAO,KAAK,OAAO,aAAa;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKA,YAAqB;AACnB,WAAO,KAAK,OAAO,aAAa;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKA,cAAuB;AACrB,WAAO,KAAK,OAAO,aAAa;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKA,mBAAmB,OAAsB;AACvC,SAAK,mBAAmB;AACxB,SAAK,OAAO,MAAM,6CAA6C,KAAK;AAAA,EACtE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,uBAA6B;AAC3B,QAAI,KAAK,kBAAkB;AACzB,WAAK,mBAAmB;AACxB,WAAK,OAAO,MAAM,gDAAgD;AAAA,IACpE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,eAAwB;AAEtB,QAAI,KAAK,kBAAkB;AACzB,aAAO;AAAA,IACT;AACA,WAAO,KAAK,OAAO,aAAa;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKA,sBAAmC;AACjC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,kBAA0B;AACxB,WAAO,KAAK,kBAAkB;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,mBAAmB,WAAqC;AACtD,UAAM,WAAW,UAAU;AAC3B,UAAM,WAAW,UAAU;AAG3B,QAAI,mBAA4C;AAChD,QAAI,YAAY,SAAS,mBAAmB,SAAS,kBAAkB,GAAG;AACxE,yBAAmB;AAAA,QACjB,SAAS,SAAS,wBAAwB;AAAA,QAC1C,QAAQ,SAAS;AAAA,QACjB,YAAY,KAAK,OAAO,SAAS,sBAAsB,KAAK,GAAG;AAAA,MACjE;AAEA,WAAK,gBAAgB;AAAA,IACvB;AAGA,UAAM,YAAwB;AAAA,MAC5B,WAAW,UAAU,aAAa;AAAA,MAClC,YAAY,UAAU,cAAc;AAAA,MACpC,WAAW,UAAU,aAAa;AAAA,MAClC,cAAc,UAAU,gBAAgB;AAAA,MACxC,WAAW,UAAU,aAAa;AAAA,MAClC,aAAa,UAAU,eAAe;AAAA,MACtC;AAAA;AAAA,MAEA,oBAAoB,CAAC;AAAA,MACrB,iBAAiB;AAAA,MACjB,qBAAqB;AAAA,IACvB;AAGA,UAAM,UAAU,KAAK,kBAAkB,SAAS;AAChD,SAAK,SAAS;AAAA,MACZ,QAAQ;AAAA,MACR,UAAU,KAAK,OAAO;AAAA,MACtB,aAAa,oBAAI,KAAK;AAAA,IACxB;AACA,QAAI,SAAS;AACX,WAAK,iBAAiB;AAAA,IACxB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,uBAAuB,WAA8B;AACnD,SAAK,oBAAoB;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,OAAwB,mBAA+B,CAAC,aAAa,aAAa,SAAS;AAAA;AAAA;AAAA;AAAA,EAK3F,MAAM,YAAY,MAA+B;AAE/C,QAAI,CAAC,mBAAkB,iBAAiB,SAAS,IAAI,GAAG;AACtD,WAAK,OAAO,KAAK,gDAAgD,IAAI;AACrE;AAAA,IACF;AACA,QAAI,KAAK,OAAO,aAAa,KAAM;AACnC,UAAM,eAAe,KAAK,OAAO;AACjC,SAAK,OAAO,WAAW;AAGvB,QAAI;AACF,YAAM,KAAK,QAAQ,QAAQ,uBAAuB,IAAI;AACtD,WAAK,OAAO,KAAK,sCAAsC,cAAc,MAAM,IAAI;AAAA,IACjF,SAAS,KAAK;AACZ,WAAK,OAAO,KAAK,gDAAgD,GAAG;AAAA,IACtE;AAGA,eAAW,YAAY,KAAK,oBAAoB;AAC9C,UAAI;AACF,iBAAS,IAAI;AAAA,MACf,SAAS,KAAK;AACZ,aAAK,OAAO,KAAK,6CAA6C,GAAG;AAAA,MACnE;AAAA,IACF;AACA,SAAK,iBAAiB,IAAI;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,UAAU,QAAgC;AAC9C,UAAM,KAAK,YAAY,SAAS,YAAY,WAAW;AAAA,EACzD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,eAAe,UAAqD;AAClE,SAAK,WAAW,IAAI,QAAQ;AAE5B,aAAS,KAAK,UAAU,CAAC;AACzB,WAAO,MAAM;AACX,WAAK,WAAW,OAAO,QAAQ;AAAA,IACjC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,iBAAiB,UAAiD;AAChE,SAAK,mBAAmB,IAAI,QAAQ;AACpC,aAAS,KAAK,OAAO,QAAQ;AAC7B,WAAO,MAAM;AACX,WAAK,mBAAmB,OAAO,QAAQ;AAAA,IACzC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,eAAe,UAAoD;AAEjE,UAAM,kBAAkB,CAAC,SAAmB;AAC1C,eAAS,SAAS,SAAS;AAAA,IAC7B;AACA,SAAK,mBAAmB,IAAI,eAAe;AAC3C,aAAS,KAAK,OAAO,aAAa,SAAS;AAC3C,WAAO,MAAM;AACX,WAAK,mBAAmB,OAAO,eAAe;AAAA,IAChD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,yBAAyB,SAAsB,OAAkB,aAAsB,mBAA6B,gBAAgC;AAClJ,UAAM,MAAM,oBAAI,KAAK;AACrB,UAAM,WAAW,QAAQ,IAAI,OAAK,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,GAAG;AAGvD,UAAM,gBAAgB,KAAK,oBAAoB,UAAU,OAAK;AAC5D,YAAM,cAAc,EAAE,QAAQ,IAAI,OAAK,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,GAAG;AAC5D,aAAO,gBAAgB;AAAA,IACzB,CAAC;AACD,QAAI,kBAAkB,IAAI;AAExB,YAAM,WAAW,KAAK,oBAAoB,aAAa;AACvD,WAAK,oBAAoB,aAAa,IAAI;AAAA,QACxC,GAAG;AAAA,QACH;AAAA,QACA,YAAY,SAAS,aAAa;AAAA,QAClC,cAAc;AAAA,QACd;AAAA,MACF;AAAA,IACF,OAAO;AAEL,YAAM,aAAgC;AAAA,QACpC,IAAI,kBAAkB,OAAO;AAAA,QAC7B;AAAA,QACA;AAAA,QACA,YAAY;AAAA,QACZ,eAAe;AAAA,QACf,cAAc;AAAA,QACd;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA,WAAK,oBAAoB,KAAK,UAAU;AAGxC,UAAI,KAAK,oBAAoB,SAAS,KAAK,oBAAoB;AAC7D,aAAK,oBAAoB,KAAK,CAAC,GAAG,MAAM,EAAE,cAAc,QAAQ,IAAI,EAAE,cAAc,QAAQ,CAAC;AAC7F,aAAK,sBAAsB,KAAK,oBAAoB,MAAM,CAAC,KAAK,kBAAkB;AAAA,MACpF;AAAA,IACF;AACA,SAAK,wBAAwB;AAC7B,SAAK,iBAAiB;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,WAAyB;AACpC,UAAM,gBAAgB,KAAK,oBAAoB;AAC/C,SAAK,sBAAsB,KAAK,oBAAoB,OAAO,OAAK,EAAE,OAAO,SAAS;AAClF,QAAI,KAAK,oBAAoB,WAAW,eAAe;AACrD,WAAK,wBAAwB;AAC7B,WAAK,iBAAiB;AAAA,IACxB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,mBAAyB;AACvB,QAAI,KAAK,oBAAoB,WAAW,EAAG;AAC3C,SAAK,sBAAsB,CAAC;AAC5B,SAAK,wBAAwB;AAC7B,SAAK,iBAAiB;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,qBAAqB,UAAuC;AAC1D,WAAO,KAAK,oBAAoB,OAAO,OAAK,EAAE,kBAAkB,SAAS,QAAQ,CAAC;AAAA,EACpF;AAAA;AAAA;AAAA;AAAA,EAKA,wBAA6C;AAC3C,WAAO,CAAC,GAAG,KAAK,mBAAmB;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA,EAKA,kBAA2B;AACzB,WAAO,KAAK,oBAAoB,SAAS;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA,EAKA,yBAAiC;AAC/B,WAAO,KAAK,oBAAoB,OAAO,OAAK,EAAE,WAAW,EAAE;AAAA,EAC7D;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,gBAAgB,UAAgE;AAC9E,SAAK,kBAAkB,IAAI,QAAQ;AAEnC,aAAS,KAAK,sBAAsB,CAAC;AACrC,WAAO,MAAM;AACX,WAAK,kBAAkB,OAAO,QAAQ;AAAA,IACxC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,uBAA6B;AAC3B,UAAM,SAAS,KAAK,IAAI,IAAI,KAAK;AACjC,UAAM,gBAAgB,KAAK,oBAAoB;AAC/C,SAAK,sBAAsB,KAAK,oBAAoB,OAAO,OAAK,EAAE,aAAa,QAAQ,IAAI,MAAM;AACjG,QAAI,KAAK,oBAAoB,WAAW,eAAe;AACrD,WAAK,OAAO,MAAM,8BAA8B,gBAAgB,KAAK,oBAAoB,MAAM,iBAAiB;AAChH,WAAK,wBAAwB;AAC7B,WAAK,iBAAiB;AAAA,IACxB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,0BAA0B,SAA4B;AAEpD,UAAM,iBAAiB,CAAC,GAAG,IAAI,IAAI,QAAQ,IAAI,OAAK,EAAE,KAAK,CAAC,CAAC;AAG7D,UAAM,oBAAoB,CAAC,GAAG,IAAI,IAAI,QAAQ,IAAI,OAAK,EAAE,EAAE,CAAC,CAAC;AAG7D,UAAM,KAAK,aAAa,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAC5E,UAAM,YAAkC;AAAA,MACtC;AAAA,MACA;AAAA,MACA,aAAa,oBAAI,KAAK;AAAA,MACtB;AAAA,MACA;AAAA,IACF;AAGA,SAAK,uBAAuB,QAAQ,SAAS;AAG7C,QAAI,KAAK,uBAAuB,SAAS,KAAK,sBAAsB;AAClE,WAAK,yBAAyB,KAAK,uBAAuB,MAAM,GAAG,KAAK,oBAAoB;AAAA,IAC9F;AACA,SAAK,0BAA0B;AAC/B,SAAK,OAAO,MAAM,mDAAmD,UAAU,EAAE,KAAK,QAAQ,MAAM,WAAW;AAAA,EACjH;AAAA;AAAA;AAAA;AAAA,EAKA,2BAAmD;AACjD,WAAO,CAAC,GAAG,KAAK,sBAAsB;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA,EAKA,wBAA8B;AAC5B,QAAI,KAAK,uBAAuB,WAAW,EAAG;AAC9C,SAAK,yBAAyB,CAAC;AAC/B,SAAK,0BAA0B;AAC/B,SAAK,OAAO,MAAM,uDAAuD;AAAA,EAC3E;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,kBAAkB,UAAoE;AACpF,SAAK,oBAAoB,IAAI,QAAQ;AAErC,aAAS,KAAK,yBAAyB,CAAC;AACxC,WAAO,MAAM;AACX,WAAK,oBAAoB,OAAO,QAAQ;AAAA,IAC1C;AAAA,EACF;AAAA;AAAA,EAIQ,kBAAkB,WAAgC;AACxD,UAAM,MAAM,KAAK,OAAO;AACxB,WAAO,IAAI,cAAc,UAAU,aAAa,IAAI,eAAe,UAAU,cAAc,IAAI,cAAc,UAAU,aAAa,IAAI,cAAc,UAAU,aAAa,IAAI,gBAAgB,UAAU,eAAe,IAAI,cAAc,QAAQ,MAAM,UAAU,cAAc,QAAQ,KAAK,IAAI,kBAAkB,YAAY,UAAU,kBAAkB,WAAW,IAAI,kBAAkB,WAAW,UAAU,kBAAkB;AAAA,EACva;AAAA,EACQ,iBAAiB,iBAAiB,OAAa;AACrD,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,sBAAsB,MAAM,KAAK;AACvC,QAAI,KAAK,cAAc;AACrB,mBAAa,KAAK,YAAY;AAC9B,WAAK,eAAe;AAAA,IACtB;AACA,UAAM,SAAS,MAAM;AACnB,WAAK,kBAAkB,KAAK,IAAI;AAChC,YAAM,SAAS,KAAK,UAAU;AAG9B,WAAK,iBAAiB,MAAM;AAG5B,iBAAW,YAAY,KAAK,YAAY;AACtC,YAAI;AACF,mBAAS,MAAM;AAAA,QACjB,SAAS,KAAK;AACZ,eAAK,OAAO,KAAK,mCAAmC,GAAG;AAAA,QACzD;AAAA,MACF;AAAA,IACF;AACA,QAAI,kBAAkB,uBAAuB,KAAK,kBAAkB;AAClE,aAAO;AAAA,IACT,OAAO;AACL,YAAM,QAAQ,KAAK,mBAAmB;AACtC,WAAK,eAAe,WAAW,QAAQ,KAAK;AAAA,IAC9C;AAAA,EACF;AAAA,EACQ,0BAAgC;AACtC,UAAM,WAAW,KAAK,sBAAsB;AAC5C,eAAW,YAAY,KAAK,mBAAmB;AAC7C,UAAI;AACF,iBAAS,QAAQ;AAAA,MACnB,SAAS,KAAK;AACZ,aAAK,OAAO,KAAK,2CAA2C,GAAG;AAAA,MACjE;AAAA,IACF;AAAA,EACF;AAAA,EACQ,4BAAkC;AACxC,UAAM,YAAY,KAAK,yBAAyB;AAChD,eAAW,YAAY,KAAK,qBAAqB;AAC/C,UAAI;AACF,iBAAS,SAAS;AAAA,MACpB,SAAS,KAAK;AACZ,aAAK,OAAO,KAAK,6CAA6C,GAAG;AAAA,MACnE;AAAA,IACF;AAAA,EACF;AACF;;;AClkBO,IAAM,mBAAN,MAAuB;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACT;AAAA,EACA,aAAa,oBAAI,IAAoC;AAAA,EACrD,eAAe;AAAA;AAAA,EAGf,iBAAgC;AAAA,EAChC,cAAc;AAAA,EACtB,YAAY,SAA8B,QAAuB,UAAmC,CAAC,GAAG;AACtG,SAAK,UAAU;AACf,SAAK,SAAS;AACd,SAAK,aAAa,QAAQ,cAAc;AACxC,SAAK,iBAAiB,QAAQ,kBAAkB;AAChD,SAAK,kBAAkB,QAAQ;AAC/B,SAAK,WAAW;AAAA,MACd,GAAG;AAAA,IACL;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,OAAsB;AAC1B,QAAI,KAAK,aAAc;AACvB,QAAI;AACF,YAAM,SAAS,MAAM,KAAK,QAAQ,QAAQ,KAAK,UAAU;AACzD,UAAI,QAAQ;AACV,cAAM,SAAS,KAAK,MAAM,MAAM;AAEhC,YAAI,OAAO,WAAW,WAAW;AAC/B,iBAAO,UAAU,YAAY,IAAI,KAAK,OAAO,UAAU,SAAS;AAAA,QAClE;AACA,aAAK,WAAW;AAAA,UACd,GAAG;AAAA,UACH,GAAG;AAAA,QACL;AACA,aAAK,OAAO,MAAM,6CAA6C;AAAA,MACjE;AAAA,IACF,SAAS,KAAK;AACZ,WAAK,OAAO,KAAK,8CAA8C,GAAG;AAAA,IACpE;AACA,SAAK,eAAe;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA,EAKA,UAAgB;AACd,SAAK,WAAW,MAAM;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,aAA0B;AACxB,WAAO;AAAA,MACL,GAAG,KAAK;AAAA,IACV;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,WAAW,MAAwC;AACvD,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,IAAI;AACJ,UAAM,aAAa,KAAK,SAAS,aAAa;AAC9C,UAAM,kBAAkB,KAAK,SAAS,mBAAmB,UAAU,IAAI;AACvE,UAAM,cAAc,KAAK,SAAS,eAAe,UAAU,IAAI;AAG/D,QAAI,sBAAsB,KAAK,SAAS;AACxC,QAAI,SAAS;AACX,UAAI,wBAAwB,MAAM;AAChC,+BAAuB,uBAAuB,kBAAkB,KAAK,cAAc;AAAA,MACrF,OAAO;AACL,8BAAsB;AAAA,MACxB;AAAA,IACF;AAGA,UAAM,aAAa;AACnB,UAAM,cAAc,wBAAwB,KAAK;AACjD,UAAM,YAAY,sBAAsB,KAAK;AAG7C,QAAI,YAA8B,KAAK,SAAS;AAChD,QAAI,CAAC,WAAW,OAAO;AACrB,YAAM,YAAY,cAAc,KAAK;AACrC,kBAAY;AAAA,QACV,MAAM;AAAA,QACN,SAAS,MAAM;AAAA,QACf,aAAa,MAAM;AAAA;AAAA,QAEnB,WAAW,oBAAI,KAAK;AAAA,QACpB,aAAa;AAAA,MACf;AAAA,IACF;AACA,SAAK,WAAW;AAAA,MACd;AAAA,MACA;AAAA,MACA;AAAA,MACA,kBAAkB;AAAA,MAClB,qBAAqB,wBAAwB,OAAO,KAAK,MAAM,mBAAmB,IAAI;AAAA,MACtF,qBAAqB,KAAK,SAAS,sBAAsB;AAAA,MACzD,mBAAmB,KAAK,SAAS,oBAAoB;AAAA,MACrD;AAAA,IACF;AACA,UAAM,KAAK,SAAS;AACpB,SAAK,iBAAiB;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAY,OAA6B;AAC7C,SAAK,WAAW;AAAA,MACd,GAAG,KAAK;AAAA,MACR,aAAa,KAAK,SAAS,cAAc;AAAA,MACzC,WAAW;AAAA,QACT,MAAM,cAAc,KAAK;AAAA,QACzB,SAAS,MAAM;AAAA,QACf,aAAa,MAAM;AAAA;AAAA,QAEnB,WAAW,oBAAI,KAAK;AAAA,QACpB,aAAa;AAAA,MACf;AAAA,IACF;AACA,UAAM,KAAK,SAAS;AACpB,SAAK,iBAAiB;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAa,gBAAuC;AACxD,UAAM,aAAa;AACnB,SAAK,WAAW;AAAA,MACd,GAAG,KAAK;AAAA,MACR,mBAAmB,KAAK,SAAS,oBAAoB,iBAAiB;AAAA,IACxE;AACA,UAAM,KAAK,SAAS;AACpB,SAAK,iBAAiB;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAuB;AAC3B,SAAK,WAAW;AAAA,MACd,GAAG;AAAA,IACL;AACA,UAAM,KAAK,SAAS;AACpB,SAAK,iBAAiB;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,gBAAsB;AACpB,QAAI,CAAC,KAAK,aAAa;AACrB,WAAK,iBAAiB,KAAK,IAAI;AAC/B,WAAK,cAAc;AAAA,IACrB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,cAA6B;AAC3B,QAAI,KAAK,eAAe,KAAK,mBAAmB,MAAM;AACpD,YAAM,WAAW,KAAK,IAAI,IAAI,KAAK;AACnC,WAAK,iBAAiB;AACtB,WAAK,cAAc;AACnB,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,mBAA4B;AAC1B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,gBAAgB,UAAuD;AACrE,SAAK,WAAW,IAAI,QAAQ;AAC5B,aAAS,KAAK,WAAW,CAAC;AAC1B,WAAO,MAAM;AACX,WAAK,WAAW,OAAO,QAAQ;AAAA,IACjC;AAAA,EACF;AAAA;AAAA,EAIA,MAAc,WAA0B;AACtC,QAAI,CAAC,KAAK,eAAgB;AAC1B,QAAI;AACF,YAAM,KAAK,QAAQ,QAAQ,KAAK,YAAY,KAAK,UAAU,KAAK,QAAQ,CAAC;AAAA,IAC3E,SAAS,KAAK;AACZ,WAAK,OAAO,KAAK,iDAAiD,GAAG;AAAA,IACvE;AAAA,EACF;AAAA,EACQ,mBAAyB;AAC/B,UAAM,UAAU,KAAK,WAAW;AAGhC,SAAK,kBAAkB,OAAO;AAG9B,eAAW,YAAY,KAAK,YAAY;AACtC,UAAI;AACF,iBAAS,OAAO;AAAA,MAClB,SAAS,KAAK;AACZ,aAAK,OAAO,KAAK,sCAAsC,GAAG;AAAA,MAC5D;AAAA,IACF;AAAA,EACF;AACF;;;ACtPO,IAAM,gBAAN,MAAoB;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACT,MAAwC;AAAA,EACxC;AAAA,EACA,cAAqD;AAAA,EACrD,aAAa,oBAAI,IAAwC;AAAA,EACzD,WAAW;AAAA,EACX,UAAU;AAAA,EAClB,YAAY,QAAuB,UAAgC,CAAC,GAAG;AACrE,SAAK,SAAS;AACd,SAAK,kBAAkB,QAAQ,mBAAmB;AAClD,SAAK,iBAAiB,QAAQ,kBAAkB;AAChD,SAAK,sBAAsB,QAAQ,uBAAuB;AAC1D,SAAK,yBAAyB,QAAQ,0BAA0B;AAChE,SAAK,iBAAiB,QAAQ;AAC9B,SAAK,UAAU;AAAA,MACb,GAAG;AAAA,IACL;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,YAAY,IAA4C;AACtD,SAAK,MAAM;AACX,QAAI,CAAC,IAAI;AAEP,WAAK,cAAc;AAAA,QACjB,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,iBAAiB,oBAAI,KAAK;AAAA,QAC1B,qBAAqB;AAAA,QACrB,mBAAmB,KAAK,QAAQ;AAAA,MAClC,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,QAAI,KAAK,SAAU;AACnB,SAAK,OAAO,KAAK,0BAA0B;AAC3C,SAAK,WAAW;AAGhB,SAAK,aAAa;AAGlB,SAAK,cAAc,YAAY,MAAM;AACnC,UAAI,CAAC,KAAK,SAAS;AACjB,aAAK,aAAa;AAAA,MACpB;AAAA,IACF,GAAG,KAAK,eAAe;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKA,OAAa;AACX,QAAI,CAAC,KAAK,SAAU;AACpB,SAAK,OAAO,KAAK,0BAA0B;AAC3C,SAAK,WAAW;AAChB,QAAI,KAAK,aAAa;AACpB,oBAAc,KAAK,WAAW;AAC9B,WAAK,cAAc;AAAA,IACrB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,SAAK,UAAU;AACf,SAAK,cAAc;AAAA,MACjB,GAAG,KAAK;AAAA,MACR,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,SAAe;AACb,SAAK,UAAU;AAEf,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAKA,UAAgB;AACd,SAAK,KAAK;AACV,SAAK,WAAW,MAAM;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,YAA8B;AAC5B,WAAO;AAAA,MACL,GAAG,KAAK;AAAA,IACV;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,YAAqB;AACnB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,WAAuC;AAC3C,WAAO,KAAK,aAAa;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,yBAA+B;AAC7B,SAAK,cAAc;AAAA,MACjB,GAAG,KAAK;AAAA,MACR,mBAAmB,KAAK,QAAQ,oBAAoB;AAAA,IACtD,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,yBAA+B;AAC7B,QAAI,KAAK,QAAQ,oBAAoB,GAAG;AACtC,WAAK,cAAc;AAAA,QACjB,GAAG,KAAK;AAAA,QACR,mBAAmB;AAAA,MACrB,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,eAAe,UAA2D;AACxE,SAAK,WAAW,IAAI,QAAQ;AAC5B,aAAS,KAAK,UAAU,CAAC;AACzB,WAAO,MAAM;AACX,WAAK,WAAW,OAAO,QAAQ;AAAA,IACjC;AAAA,EACF;AAAA;AAAA,EAIA,MAAc,eAA2C;AACvD,QAAI,CAAC,KAAK,OAAO,KAAK,SAAS;AAC7B,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,IAAI,MAAM,kCAAkC;AAAA,QACnD,WAAW,oBAAI,KAAK;AAAA,MACtB;AAAA,IACF;AACA,UAAM,YAAY,KAAK,IAAI;AAC3B,UAAM,YAAY,oBAAI,KAAK;AAC3B,QAAI;AAEF,YAAM,KAAK,aAAa,KAAK,IAAI,IAAI,UAAU,GAAG,KAAK,cAAc;AACrE,YAAM,YAAY,KAAK,IAAI,IAAI;AAG/B,YAAM,SAAqC,YAAY,KAAK,sBAAsB,YAAY;AAC9F,WAAK,cAAc;AAAA,QACjB;AAAA,QACA,SAAS;AAAA,QACT,iBAAiB;AAAA,QACjB,qBAAqB;AAAA,QACrB,mBAAmB,KAAK,QAAQ;AAAA,MAClC,CAAC;AACD,aAAO;AAAA,QACL,SAAS;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,IACF,SAAS,KAAK;AACZ,YAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAChE,WAAK,OAAO,KAAK,wCAAwC,MAAM,OAAO;AACtE,YAAM,sBAAsB,KAAK,QAAQ,sBAAsB;AAC/D,YAAM,SAAqC,uBAAuB,KAAK,yBAAyB,iBAAiB;AACjH,WAAK,cAAc;AAAA,QACjB;AAAA,QACA,SAAS;AAAA,QACT,iBAAiB;AAAA,QACjB;AAAA,QACA,mBAAmB,KAAK,QAAQ;AAAA,MAClC,CAAC;AACD,aAAO;AAAA,QACL,SAAS;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACQ,cAAc,QAAgC;AACpD,UAAM,UAAU,KAAK,kBAAkB,MAAM;AAC7C,SAAK,UAAU;AACf,QAAI,SAAS;AACX,WAAK,iBAAiB;AAAA,IACxB;AAAA,EACF;AAAA,EACQ,kBAAkB,WAAsC;AAC9D,UAAM,MAAM,KAAK;AACjB,WAAO,IAAI,WAAW,UAAU,UAAU,IAAI,YAAY,UAAU,WAAW,IAAI,wBAAwB,UAAU,uBAAuB,IAAI,sBAAsB,UAAU;AAAA,EAClL;AAAA,EACQ,mBAAyB;AAC/B,UAAM,SAAS,KAAK,UAAU;AAG9B,SAAK,iBAAiB,MAAM;AAG5B,eAAW,YAAY,KAAK,YAAY;AACtC,UAAI;AACF,iBAAS,MAAM;AAAA,MACjB,SAAS,KAAK;AACZ,aAAK,OAAO,KAAK,mCAAmC,GAAG;AAAA,MACzD;AAAA,IACF;AAAA,EACF;AAAA,EACQ,aAAgB,SAAqB,WAA+B;AAC1E,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,QAAQ,WAAW,MAAM;AAC7B,eAAO,IAAI,MAAM,8BAA8B,SAAS,IAAI,CAAC;AAAA,MAC/D,GAAG,SAAS;AACZ,cAAQ,KAAK,YAAU;AACrB,qBAAa,KAAK;AAClB,gBAAQ,MAAM;AAAA,MAChB,GAAG,WAAS;AACV,qBAAa,KAAK;AAClB,eAAO,KAAK;AAAA,MACd,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AACF;","names":[]}
|