@pol-studios/powersync 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
package/dist/index.native.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { A as AbstractPowerSyncDatabase, k as CacheStats, i as ClassifiedError, j as CompactResult, h as CompletedTransaction, b as ConnectionHealth, o as CountRow, C as CrudEntry, l as CrudTransaction, n as DbStatRow, D as DownloadProgress, E as EntitySyncState, F as FailedTransaction, r as FreelistCountRow, s as IntegrityCheckRow, I as IntegrityResult, q as PageCountRow, p as PageSizeRow, P as PowerSyncBackendConnector, m as SqliteTableRow, c as StorageInfo, d as StorageQuota, f as SyncError, g as SyncErrorType, e as SyncMetrics, S as SyncMode, a as SyncStatus, T as TableCacheStats } from './types-afHtE1U_.js';
|
|
2
2
|
export { ATTACHMENT_DOWNLOAD_TIMEOUT_MS, ATTACHMENT_RETRY_DELAY_MS, AttachmentError, COMPRESSION_MAX_WIDTH, COMPRESSION_SKIP_SIZE_BYTES, COMPRESSION_TARGET_SIZE_BYTES, ConfigurationError, ConnectorError, DEFAULT_ATTACHMENT_CACHE_SIZE, DEFAULT_ATTACHMENT_CONCURRENCY, DEFAULT_COMPRESSION_QUALITY, DEFAULT_MAX_RETRY_ATTEMPTS, DEFAULT_RETRY_BACKOFF_MULTIPLIER, DEFAULT_RETRY_BASE_DELAY_MS, DEFAULT_RETRY_MAX_DELAY_MS, DEFAULT_SYNC_INTERVAL_MS, DEFAULT_SYNC_MODE, DOWNLOAD_STOP_THRESHOLD, EVICTION_TRIGGER_THRESHOLD, HEALTH_CHECK_INTERVAL_MS, HEALTH_CHECK_TIMEOUT_MS, InitializationError, LATENCY_DEGRADED_THRESHOLD_MS, MAX_CONSECUTIVE_FAILURES, PlatformAdapterError, PowerSyncError, STATS_CACHE_TTL_MS, STATUS_NOTIFY_THROTTLE_MS, STORAGE_CRITICAL_THRESHOLD, STORAGE_KEY_ATTACHMENT_SETTINGS, STORAGE_KEY_ENABLED, STORAGE_KEY_METRICS, STORAGE_KEY_PAUSED, STORAGE_KEY_PREFIX, STORAGE_KEY_SYNC_MODE, STORAGE_WARNING_THRESHOLD, SyncOperationError, classifyError, classifySupabaseError, createSyncError, extractEntityIds, extractTableNames, generateFailureId, toSyncOperationError } from './core/index.js';
|
|
3
|
-
export { e as ConflictBus, f as ConflictCheckResult, i as ConflictDetectionConfig, h as ConflictHandler, j as ConflictListener, g as ConflictResolution, C as ConnectorConfig, c as CrudHandler, F as FieldConflict, P as PowerSyncCredentials, R as ResolutionListener, b as SchemaRouter, S as SupabaseConnector, a as SupabaseConnectorOptions, d as defaultSchemaRouter } from './index-
|
|
3
|
+
export { e as ConflictBus, f as ConflictCheckResult, i as ConflictDetectionConfig, h as ConflictHandler, j as ConflictListener, g as ConflictResolution, C as ConnectorConfig, c as CrudHandler, F as FieldConflict, P as PowerSyncCredentials, R as ResolutionListener, b as SchemaRouter, S as SupabaseConnector, a as SupabaseConnectorOptions, d as defaultSchemaRouter } from './index-Cb-NI0Ct.js';
|
|
4
4
|
export { AttachmentQueue, AttachmentQueueConfig, AttachmentRecord, AttachmentSourceConfig, AttachmentState, AttachmentStatsRow, AttachmentStorageAdapter, AttachmentSyncStats, AttachmentSyncStatus, CacheConfig, CacheFileRow, CachedSizeRow, CompressionConfig, DEFAULT_CACHE_CONFIG, DEFAULT_COMPRESSION_CONFIG, DEFAULT_DOWNLOAD_CONFIG, DownloadConfig, DownloadPhase, DownloadStatus, EvictRow, IdRow } from './attachments/index.js';
|
|
5
5
|
export { e as HealthCheckResult, H as HealthMonitorOptions, M as MetricsCollectorOptions, P as PowerSyncRawStatus, c as SyncControlActions, f as SyncEvent, g as SyncEventListener, d as SyncOperationData, S as SyncScope, a as SyncStatusState, b as SyncStatusTrackerOptions, U as Unsubscribe } from './types-Cd7RhNqf.js';
|
|
6
6
|
export { HealthMonitor, MetricsCollector, SyncStatusTracker } from './sync/index.js';
|
package/dist/index.native.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
ConflictBus
|
|
3
|
-
} from "./chunk-42IJ25Q4.js";
|
|
1
|
+
import "./chunk-OLHGI472.js";
|
|
4
2
|
import "./chunk-32OLICZO.js";
|
|
5
3
|
import {
|
|
6
4
|
AttachmentQueueContext,
|
|
5
|
+
ConflictBus,
|
|
7
6
|
ConnectionHealthContext,
|
|
8
7
|
PowerSyncContext,
|
|
9
8
|
PowerSyncProvider,
|
|
@@ -25,14 +24,14 @@ import {
|
|
|
25
24
|
useSyncMode,
|
|
26
25
|
useSyncStatus,
|
|
27
26
|
useUploadStatus
|
|
28
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-GMFDCVMZ.js";
|
|
29
28
|
import {
|
|
30
29
|
AttachmentQueue,
|
|
31
30
|
AttachmentState,
|
|
32
31
|
DEFAULT_CACHE_CONFIG,
|
|
33
32
|
DEFAULT_COMPRESSION_CONFIG,
|
|
34
33
|
DEFAULT_DOWNLOAD_CONFIG
|
|
35
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-V6LJ6MR2.js";
|
|
36
35
|
import "./chunk-7JQZBZ5N.js";
|
|
37
36
|
import {
|
|
38
37
|
DEFAULT_CONNECTION_HEALTH,
|
|
@@ -42,7 +41,7 @@ import {
|
|
|
42
41
|
HealthMonitor,
|
|
43
42
|
MetricsCollector,
|
|
44
43
|
SyncStatusTracker
|
|
45
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-OTJXIRWX.js";
|
|
46
45
|
import "./chunk-W7HSR35B.js";
|
|
47
46
|
import {
|
|
48
47
|
ATTACHMENT_DOWNLOAD_TIMEOUT_MS,
|
|
@@ -75,10 +74,10 @@ import {
|
|
|
75
74
|
STORAGE_KEY_PREFIX,
|
|
76
75
|
STORAGE_KEY_SYNC_MODE,
|
|
77
76
|
STORAGE_WARNING_THRESHOLD
|
|
78
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-EJ23MXPQ.js";
|
|
79
78
|
import {
|
|
80
79
|
createNativePlatformAdapter
|
|
81
|
-
} from "./chunk-
|
|
80
|
+
} from "./chunk-3AYXHQ4W.js";
|
|
82
81
|
import "./chunk-VJCL2SWD.js";
|
|
83
82
|
import {
|
|
84
83
|
SupabaseConnector,
|
|
@@ -87,7 +86,7 @@ import {
|
|
|
87
86
|
fetchServerVersion,
|
|
88
87
|
getLocalVersion,
|
|
89
88
|
hasVersionColumn
|
|
90
|
-
} from "./chunk-
|
|
89
|
+
} from "./chunk-C2RSTGDC.js";
|
|
91
90
|
import {
|
|
92
91
|
AttachmentError,
|
|
93
92
|
ConfigurationError,
|
|
@@ -103,7 +102,7 @@ import {
|
|
|
103
102
|
extractTableNames,
|
|
104
103
|
generateFailureId,
|
|
105
104
|
toSyncOperationError
|
|
106
|
-
} from "./chunk-
|
|
105
|
+
} from "./chunk-FPTDATY5.js";
|
|
107
106
|
export {
|
|
108
107
|
ATTACHMENT_DOWNLOAD_TIMEOUT_MS,
|
|
109
108
|
ATTACHMENT_RETRY_DELAY_MS,
|
package/dist/index.web.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { A as AbstractPowerSyncDatabase, k as CacheStats, i as ClassifiedError, j as CompactResult, h as CompletedTransaction, b as ConnectionHealth, o as CountRow, C as CrudEntry, l as CrudTransaction, n as DbStatRow, D as DownloadProgress, E as EntitySyncState, F as FailedTransaction, r as FreelistCountRow, s as IntegrityCheckRow, I as IntegrityResult, q as PageCountRow, p as PageSizeRow, P as PowerSyncBackendConnector, m as SqliteTableRow, c as StorageInfo, d as StorageQuota, f as SyncError, g as SyncErrorType, e as SyncMetrics, S as SyncMode, a as SyncStatus, T as TableCacheStats } from './types-afHtE1U_.js';
|
|
2
2
|
export { ATTACHMENT_DOWNLOAD_TIMEOUT_MS, ATTACHMENT_RETRY_DELAY_MS, AttachmentError, COMPRESSION_MAX_WIDTH, COMPRESSION_SKIP_SIZE_BYTES, COMPRESSION_TARGET_SIZE_BYTES, ConfigurationError, ConnectorError, DEFAULT_ATTACHMENT_CACHE_SIZE, DEFAULT_ATTACHMENT_CONCURRENCY, DEFAULT_COMPRESSION_QUALITY, DEFAULT_MAX_RETRY_ATTEMPTS, DEFAULT_RETRY_BACKOFF_MULTIPLIER, DEFAULT_RETRY_BASE_DELAY_MS, DEFAULT_RETRY_MAX_DELAY_MS, DEFAULT_SYNC_INTERVAL_MS, DEFAULT_SYNC_MODE, DOWNLOAD_STOP_THRESHOLD, EVICTION_TRIGGER_THRESHOLD, HEALTH_CHECK_INTERVAL_MS, HEALTH_CHECK_TIMEOUT_MS, InitializationError, LATENCY_DEGRADED_THRESHOLD_MS, MAX_CONSECUTIVE_FAILURES, PlatformAdapterError, PowerSyncError, STATS_CACHE_TTL_MS, STATUS_NOTIFY_THROTTLE_MS, STORAGE_CRITICAL_THRESHOLD, STORAGE_KEY_ATTACHMENT_SETTINGS, STORAGE_KEY_ENABLED, STORAGE_KEY_METRICS, STORAGE_KEY_PAUSED, STORAGE_KEY_PREFIX, STORAGE_KEY_SYNC_MODE, STORAGE_WARNING_THRESHOLD, SyncOperationError, classifyError, classifySupabaseError, createSyncError, extractEntityIds, extractTableNames, generateFailureId, toSyncOperationError } from './core/index.js';
|
|
3
|
-
export { e as ConflictBus, f as ConflictCheckResult, i as ConflictDetectionConfig, h as ConflictHandler, j as ConflictListener, g as ConflictResolution, C as ConnectorConfig, c as CrudHandler, F as FieldConflict, P as PowerSyncCredentials, R as ResolutionListener, b as SchemaRouter, S as SupabaseConnector, a as SupabaseConnectorOptions, d as defaultSchemaRouter } from './index-
|
|
3
|
+
export { e as ConflictBus, f as ConflictCheckResult, i as ConflictDetectionConfig, h as ConflictHandler, j as ConflictListener, g as ConflictResolution, C as ConnectorConfig, c as CrudHandler, F as FieldConflict, P as PowerSyncCredentials, R as ResolutionListener, b as SchemaRouter, S as SupabaseConnector, a as SupabaseConnectorOptions, d as defaultSchemaRouter } from './index-Cb-NI0Ct.js';
|
|
4
4
|
export { AttachmentQueue, AttachmentQueueConfig, AttachmentRecord, AttachmentSourceConfig, AttachmentState, AttachmentStatsRow, AttachmentStorageAdapter, AttachmentSyncStats, AttachmentSyncStatus, CacheConfig, CacheFileRow, CachedSizeRow, CompressionConfig, DEFAULT_CACHE_CONFIG, DEFAULT_COMPRESSION_CONFIG, DEFAULT_DOWNLOAD_CONFIG, DownloadConfig, DownloadPhase, DownloadStatus, EvictRow, IdRow } from './attachments/index.js';
|
|
5
5
|
export { e as HealthCheckResult, H as HealthMonitorOptions, M as MetricsCollectorOptions, P as PowerSyncRawStatus, c as SyncControlActions, f as SyncEvent, g as SyncEventListener, d as SyncOperationData, S as SyncScope, a as SyncStatusState, b as SyncStatusTrackerOptions, U as Unsubscribe } from './types-Cd7RhNqf.js';
|
|
6
6
|
export { HealthMonitor, MetricsCollector, SyncStatusTracker } from './sync/index.js';
|
package/dist/index.web.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
ConflictBus
|
|
3
|
-
} from "./chunk-42IJ25Q4.js";
|
|
1
|
+
import "./chunk-OLHGI472.js";
|
|
4
2
|
import "./chunk-32OLICZO.js";
|
|
5
3
|
import {
|
|
6
4
|
AttachmentQueueContext,
|
|
5
|
+
ConflictBus,
|
|
7
6
|
ConnectionHealthContext,
|
|
8
7
|
PowerSyncContext,
|
|
9
8
|
PowerSyncProvider,
|
|
@@ -25,14 +24,14 @@ import {
|
|
|
25
24
|
useSyncMode,
|
|
26
25
|
useSyncStatus,
|
|
27
26
|
useUploadStatus
|
|
28
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-GMFDCVMZ.js";
|
|
29
28
|
import {
|
|
30
29
|
AttachmentQueue,
|
|
31
30
|
AttachmentState,
|
|
32
31
|
DEFAULT_CACHE_CONFIG,
|
|
33
32
|
DEFAULT_COMPRESSION_CONFIG,
|
|
34
33
|
DEFAULT_DOWNLOAD_CONFIG
|
|
35
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-V6LJ6MR2.js";
|
|
36
35
|
import "./chunk-7JQZBZ5N.js";
|
|
37
36
|
import {
|
|
38
37
|
DEFAULT_CONNECTION_HEALTH,
|
|
@@ -42,7 +41,7 @@ import {
|
|
|
42
41
|
HealthMonitor,
|
|
43
42
|
MetricsCollector,
|
|
44
43
|
SyncStatusTracker
|
|
45
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-OTJXIRWX.js";
|
|
46
45
|
import "./chunk-W7HSR35B.js";
|
|
47
46
|
import {
|
|
48
47
|
ATTACHMENT_DOWNLOAD_TIMEOUT_MS,
|
|
@@ -75,10 +74,10 @@ import {
|
|
|
75
74
|
STORAGE_KEY_PREFIX,
|
|
76
75
|
STORAGE_KEY_SYNC_MODE,
|
|
77
76
|
STORAGE_WARNING_THRESHOLD
|
|
78
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-EJ23MXPQ.js";
|
|
79
78
|
import {
|
|
80
79
|
createWebPlatformAdapter
|
|
81
|
-
} from "./chunk-
|
|
80
|
+
} from "./chunk-7EMDVIZX.js";
|
|
82
81
|
import "./chunk-VJCL2SWD.js";
|
|
83
82
|
import {
|
|
84
83
|
SupabaseConnector,
|
|
@@ -87,7 +86,7 @@ import {
|
|
|
87
86
|
fetchServerVersion,
|
|
88
87
|
getLocalVersion,
|
|
89
88
|
hasVersionColumn
|
|
90
|
-
} from "./chunk-
|
|
89
|
+
} from "./chunk-C2RSTGDC.js";
|
|
91
90
|
import {
|
|
92
91
|
AttachmentError,
|
|
93
92
|
ConfigurationError,
|
|
@@ -103,7 +102,7 @@ import {
|
|
|
103
102
|
extractTableNames,
|
|
104
103
|
generateFailureId,
|
|
105
104
|
toSyncOperationError
|
|
106
|
-
} from "./chunk-
|
|
105
|
+
} from "./chunk-FPTDATY5.js";
|
|
107
106
|
export {
|
|
108
107
|
ATTACHMENT_DOWNLOAD_TIMEOUT_MS,
|
|
109
108
|
ATTACHMENT_RETRY_DELAY_MS,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/platform/types.ts"],"sourcesContent":["/**\n * Platform Adapter Types for @pol-studios/powersync\n *\n * This module defines the interfaces that platform-specific adapters must implement.\n * Consumers provide an adapter for their platform (React Native or Web) that handles\n * platform-specific operations like file system access, async storage, and network status.\n */\n\nimport type { AbstractPowerSyncDatabase } from '../core/types';\n\n// ─── Database Factory Options ────────────────────────────────────────────────\n\n/**\n * Options for creating a PowerSync database instance\n */\nexport interface DatabaseOptions {\n /** Database file name (e.g., \"pol-offline.db\") */\n dbFilename: string;\n /** PowerSync schema definition */\n schema: unknown; // Schema type from @powersync/common\n /** Optional: Encryption key for SQLite */\n encryptionKey?: string;\n}\n\n// ─── File System Adapter ─────────────────────────────────────────────────────\n\n/**\n * Information about a file or directory\n */\nexport interface FileInfo {\n /** Whether the file/directory exists */\n exists: boolean;\n /** Size in bytes (0 for directories) */\n size: number;\n /** Last modification time */\n modificationTime?: Date;\n /** Whether this is a directory */\n isDirectory: boolean;\n}\n\n/**\n * File system operations interface.\n *\n * React Native: Implemented using expo-file-system\n * Web: Implemented using IndexedDB or Cache API\n */\nexport interface FileSystemAdapter {\n /**\n * Read a file's contents\n * @param uri - The file URI/path\n * @param encoding - How to encode the result ('base64' or 'utf8')\n */\n readFile(uri: string, encoding?: 'base64' | 'utf8'): Promise<string>;\n\n /**\n * Write data to a file (creates parent directories if needed)\n * @param uri - The file URI/path\n * @param data - The data to write\n * @param encoding - How the data is encoded ('base64' or 'utf8')\n */\n writeFile(uri: string, data: string, encoding?: 'base64' | 'utf8'): Promise<void>;\n\n /**\n * Delete a file\n * @param uri - The file URI/path\n */\n deleteFile(uri: string): Promise<void>;\n\n /**\n * Copy a file from source to destination\n * @param source - Source file URI/path\n * @param destination - Destination file URI/path\n */\n copyFile(source: string, destination: string): Promise<void>;\n\n /**\n * Move a file from source to destination\n * @param source - Source file URI/path\n * @param destination - Destination file URI/path\n */\n moveFile(source: string, destination: string): Promise<void>;\n\n /**\n * Get information about a file or directory\n * @param uri - The file URI/path\n * @returns File info or null if it doesn't exist\n */\n getFileInfo(uri: string): Promise<FileInfo | null>;\n\n /**\n * Create a directory\n * @param uri - The directory URI/path\n * @param options - Options for directory creation\n */\n makeDirectory(uri: string, options?: {
|
|
1
|
+
{"version":3,"sources":["../../src/platform/types.ts"],"sourcesContent":["/**\n * Platform Adapter Types for @pol-studios/powersync\n *\n * This module defines the interfaces that platform-specific adapters must implement.\n * Consumers provide an adapter for their platform (React Native or Web) that handles\n * platform-specific operations like file system access, async storage, and network status.\n */\n\nimport type { AbstractPowerSyncDatabase } from '../core/types';\n\n// ─── Database Factory Options ────────────────────────────────────────────────\n\n/**\n * Options for creating a PowerSync database instance\n */\nexport interface DatabaseOptions {\n /** Database file name (e.g., \"pol-offline.db\") */\n dbFilename: string;\n /** PowerSync schema definition */\n schema: unknown; // Schema type from @powersync/common\n /** Optional: Encryption key for SQLite */\n encryptionKey?: string;\n}\n\n// ─── File System Adapter ─────────────────────────────────────────────────────\n\n/**\n * Information about a file or directory\n */\nexport interface FileInfo {\n /** Whether the file/directory exists */\n exists: boolean;\n /** Size in bytes (0 for directories) */\n size: number;\n /** Last modification time */\n modificationTime?: Date;\n /** Whether this is a directory */\n isDirectory: boolean;\n}\n\n/**\n * File system operations interface.\n *\n * React Native: Implemented using expo-file-system\n * Web: Implemented using IndexedDB or Cache API\n */\nexport interface FileSystemAdapter {\n /**\n * Read a file's contents\n * @param uri - The file URI/path\n * @param encoding - How to encode the result ('base64' or 'utf8')\n */\n readFile(uri: string, encoding?: 'base64' | 'utf8'): Promise<string>;\n\n /**\n * Write data to a file (creates parent directories if needed)\n * @param uri - The file URI/path\n * @param data - The data to write\n * @param encoding - How the data is encoded ('base64' or 'utf8')\n */\n writeFile(uri: string, data: string, encoding?: 'base64' | 'utf8'): Promise<void>;\n\n /**\n * Delete a file\n * @param uri - The file URI/path\n */\n deleteFile(uri: string): Promise<void>;\n\n /**\n * Copy a file from source to destination\n * @param source - Source file URI/path\n * @param destination - Destination file URI/path\n */\n copyFile(source: string, destination: string): Promise<void>;\n\n /**\n * Move a file from source to destination\n * @param source - Source file URI/path\n * @param destination - Destination file URI/path\n */\n moveFile(source: string, destination: string): Promise<void>;\n\n /**\n * Get information about a file or directory\n * @param uri - The file URI/path\n * @returns File info or null if it doesn't exist\n */\n getFileInfo(uri: string): Promise<FileInfo | null>;\n\n /**\n * Create a directory\n * @param uri - The directory URI/path\n * @param options - Options for directory creation\n */\n makeDirectory(uri: string, options?: {\n intermediates?: boolean;\n }): Promise<void>;\n\n /**\n * Get the documents directory path for this platform\n */\n getDocumentsDirectory(): string;\n\n /**\n * Get the cache directory path for this platform\n */\n getCacheDirectory(): string;\n\n /**\n * Get the free disk space in bytes\n */\n getFreeDiskSpace(): Promise<number>;\n}\n\n// ─── Async Storage Adapter ───────────────────────────────────────────────────\n\n/**\n * Key-value storage interface for persisting preferences and state.\n *\n * React Native: Implemented using @react-native-async-storage/async-storage\n * Web: Implemented using localStorage\n */\nexport interface AsyncStorageAdapter {\n /**\n * Get a value by key\n * @param key - The storage key\n * @returns The stored value or null if not found\n */\n getItem(key: string): Promise<string | null>;\n\n /**\n * Set a value by key\n * @param key - The storage key\n * @param value - The value to store\n */\n setItem(key: string, value: string): Promise<void>;\n\n /**\n * Remove a value by key\n * @param key - The storage key\n */\n removeItem(key: string): Promise<void>;\n\n /**\n * Get multiple values by keys\n * @param keys - Array of storage keys\n * @returns Array of [key, value] tuples\n */\n multiGet(keys: string[]): Promise<[string, string | null][]>;\n\n /**\n * Set multiple values\n * @param entries - Array of [key, value] tuples\n */\n multiSet(entries: [string, string][]): Promise<void>;\n}\n\n// ─── Network Adapter ─────────────────────────────────────────────────────────\n\n/** Network connection types */\nexport type ConnectionType = 'wifi' | 'cellular' | 'ethernet' | 'unknown' | 'none';\n\n/**\n * Network connectivity monitoring interface.\n *\n * React Native: Implemented using @react-native-community/netinfo\n * Web: Implemented using navigator.onLine + online/offline events\n */\nexport interface NetworkAdapter {\n /**\n * Check if the device is currently connected to the internet\n */\n isConnected(): Promise<boolean>;\n\n /**\n * Get the current connection type\n */\n getConnectionType(): Promise<ConnectionType>;\n\n /**\n * Add a listener for connection status changes\n * @param callback - Called when connection status changes\n * @returns Unsubscribe function\n */\n addConnectionListener(callback: (isConnected: boolean) => void): () => void;\n}\n\n// ─── Logger Adapter ──────────────────────────────────────────────────────────\n\n/**\n * Logging interface for debugging and monitoring.\n * Allows consumers to use their existing logging infrastructure.\n */\nexport interface LoggerAdapter {\n /**\n * Log a debug message (verbose, for development)\n */\n debug(message: string, ...args: unknown[]): void;\n\n /**\n * Log an info message (general information)\n */\n info(message: string, ...args: unknown[]): void;\n\n /**\n * Log a warning message (potential issues)\n */\n warn(message: string, ...args: unknown[]): void;\n\n /**\n * Log an error message (failures)\n */\n error(message: string, ...args: unknown[]): void;\n}\n\n// ─── Image Processor Adapter ─────────────────────────────────────────────────\n\n/**\n * Result of an image compression operation\n */\nexport interface CompressedImage {\n /** URI/path to the compressed image */\n uri: string;\n /** Width of the compressed image in pixels */\n width: number;\n /** Height of the compressed image in pixels */\n height: number;\n}\n\n/**\n * Options for image compression\n */\nexport interface CompressionOptions {\n /** Compression quality (0.0 to 1.0, where 1.0 is best quality) */\n quality: number;\n /** Maximum width in pixels (will scale down if larger) */\n maxWidth?: number;\n /** Maximum height in pixels (will scale down if larger) */\n maxHeight?: number;\n /** Output format */\n format?: 'jpeg' | 'png' | 'webp';\n}\n\n/**\n * Image processing interface for attachment compression.\n *\n * React Native: Implemented using expo-image-manipulator\n * Web: Implemented using canvas API\n *\n * This is optional - if not provided, attachments won't be compressed.\n */\nexport interface ImageProcessorAdapter {\n /**\n * Compress an image\n * @param uri - The source image URI/path\n * @param options - Compression options\n * @returns The compressed image result\n */\n compress(uri: string, options: CompressionOptions): Promise<CompressedImage>;\n}\n\n// ─── Main Platform Adapter Interface ─────────────────────────────────────────\n\n/**\n * Platform-specific adapter that consumers must provide.\n * This allows the package to work across React Native and Web.\n *\n * @example React Native\n * ```typescript\n * import { createNativePlatformAdapter } from '@pol-studios/powersync/platform';\n *\n * const platform = createNativePlatformAdapter(logger);\n * ```\n *\n * @example Web\n * ```typescript\n * import { createWebPlatformAdapter } from '@pol-studios/powersync/platform';\n *\n * const platform = createWebPlatformAdapter(logger);\n * ```\n */\nexport interface PlatformAdapter {\n /**\n * Create a PowerSync database instance for this platform.\n *\n * React Native: Uses PowerSyncDatabase from @powersync/react-native\n * Web: Uses PowerSyncDatabase from @powersync/web\n *\n * @param options - Database creation options\n * @returns Initialized PowerSync database\n */\n createDatabase(options: DatabaseOptions): Promise<AbstractPowerSyncDatabase>;\n\n /**\n * File system operations for attachment caching.\n */\n fileSystem: FileSystemAdapter;\n\n /**\n * Key-value storage for preferences and metrics persistence.\n */\n storage: AsyncStorageAdapter;\n\n /**\n * Network connectivity monitoring.\n */\n network: NetworkAdapter;\n\n /**\n * Logging interface.\n */\n logger: LoggerAdapter;\n\n /**\n * Optional: Image compression for attachment optimization.\n * If not provided, attachments won't be compressed.\n */\n imageProcessor?: ImageProcessorAdapter;\n}\n\n// ─── Platform Detection ──────────────────────────────────────────────────────\n\n/** Supported platform types */\nexport type PlatformType = 'react-native' | 'web' | 'unknown';\n\n/**\n * Detect the current platform based on environment\n */\nexport function detectPlatform(): PlatformType {\n // Check for React Native\n if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') {\n return 'react-native';\n }\n\n // Check for browser environment\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n return 'web';\n }\n return 'unknown';\n}"],"mappings":";AAwUO,SAAS,iBAA+B;AAE7C,MAAI,OAAO,cAAc,eAAe,UAAU,YAAY,eAAe;AAC3E,WAAO;AAAA,EACT;AAGA,MAAI,OAAO,WAAW,eAAe,OAAO,aAAa,aAAa;AACpE,WAAO;AAAA,EACT;AACA,SAAO;AACT;","names":[]}
|
package/dist/provider/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { SupabaseClient } from '@supabase/supabase-js';
|
|
|
2
2
|
import { QueryClient } from '@tanstack/react-query';
|
|
3
3
|
import { A as AbstractPowerSyncDatabase, a as SyncStatus, C as CrudEntry, S as SyncMode, F as FailedTransaction, h as CompletedTransaction, b as ConnectionHealth, e as SyncMetrics, D as DownloadProgress, E as EntitySyncState, f as SyncError } from '../types-afHtE1U_.js';
|
|
4
4
|
import { PlatformAdapter } from '../platform/index.js';
|
|
5
|
-
import { C as ConnectorConfig, S as SupabaseConnector } from '../index-
|
|
5
|
+
import { C as ConnectorConfig, S as SupabaseConnector, e as ConflictBus } from '../index-Cb-NI0Ct.js';
|
|
6
6
|
import { AttachmentQueueConfig, AttachmentQueue } from '../attachments/index.js';
|
|
7
7
|
import * as react from 'react';
|
|
8
8
|
import react__default from 'react';
|
|
@@ -144,6 +144,11 @@ interface PowerSyncContextValue<TSchema = unknown> {
|
|
|
144
144
|
* The platform adapter instance.
|
|
145
145
|
*/
|
|
146
146
|
platform: PlatformAdapter;
|
|
147
|
+
/**
|
|
148
|
+
* The conflict bus for subscribing to conflict events.
|
|
149
|
+
* Use this to wire up conflict UI components.
|
|
150
|
+
*/
|
|
151
|
+
conflictBus: ConflictBus;
|
|
147
152
|
}
|
|
148
153
|
/**
|
|
149
154
|
* Value provided by SyncStatusContext.
|
package/dist/provider/index.js
CHANGED
|
@@ -21,17 +21,17 @@ import {
|
|
|
21
21
|
useSyncMode,
|
|
22
22
|
useSyncStatus,
|
|
23
23
|
useUploadStatus
|
|
24
|
-
} from "../chunk-
|
|
25
|
-
import "../chunk-
|
|
24
|
+
} from "../chunk-GMFDCVMZ.js";
|
|
25
|
+
import "../chunk-V6LJ6MR2.js";
|
|
26
26
|
import {
|
|
27
27
|
DEFAULT_CONNECTION_HEALTH,
|
|
28
28
|
DEFAULT_SYNC_CONFIG,
|
|
29
29
|
DEFAULT_SYNC_METRICS,
|
|
30
30
|
DEFAULT_SYNC_STATUS
|
|
31
|
-
} from "../chunk-
|
|
32
|
-
import "../chunk-
|
|
33
|
-
import "../chunk-
|
|
34
|
-
import "../chunk-
|
|
31
|
+
} from "../chunk-OTJXIRWX.js";
|
|
32
|
+
import "../chunk-EJ23MXPQ.js";
|
|
33
|
+
import "../chunk-C2RSTGDC.js";
|
|
34
|
+
import "../chunk-FPTDATY5.js";
|
|
35
35
|
export {
|
|
36
36
|
AttachmentQueueContext,
|
|
37
37
|
ConnectionHealthContext,
|
package/dist/sync/index.js
CHANGED
|
@@ -3,9 +3,9 @@ import {
|
|
|
3
3
|
HealthMonitor,
|
|
4
4
|
MetricsCollector,
|
|
5
5
|
SyncStatusTracker
|
|
6
|
-
} from "../chunk-
|
|
7
|
-
import "../chunk-
|
|
8
|
-
import "../chunk-
|
|
6
|
+
} from "../chunk-OTJXIRWX.js";
|
|
7
|
+
import "../chunk-EJ23MXPQ.js";
|
|
8
|
+
import "../chunk-FPTDATY5.js";
|
|
9
9
|
export {
|
|
10
10
|
HealthMonitor,
|
|
11
11
|
MetricsCollector,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pol-studios/powersync",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Enterprise PowerSync integration for offline-first applications",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -112,9 +112,11 @@
|
|
|
112
112
|
"@pol-studios/db": "workspace:*"
|
|
113
113
|
},
|
|
114
114
|
"devDependencies": {
|
|
115
|
+
"@babel/core": "^7.28.0",
|
|
115
116
|
"@powersync/common": "^1.0.0",
|
|
116
117
|
"@powersync/op-sqlite": "^0.8.0",
|
|
117
118
|
"@types/react": "^18.0.0",
|
|
119
|
+
"babel-plugin-react-compiler": "^1.0.0",
|
|
118
120
|
"tsup": "^8.0.0",
|
|
119
121
|
"typescript": "^5.0.0"
|
|
120
122
|
},
|
package/dist/chunk-42IJ25Q4.js
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
// src/conflicts/conflict-bus.ts
|
|
2
|
-
var ConflictBus = class {
|
|
3
|
-
conflictListeners = /* @__PURE__ */ new Set();
|
|
4
|
-
resolutionListeners = /* @__PURE__ */ new Set();
|
|
5
|
-
/**
|
|
6
|
-
* Subscribe to conflict detection events.
|
|
7
|
-
* @returns Unsubscribe function
|
|
8
|
-
*/
|
|
9
|
-
onConflict(listener) {
|
|
10
|
-
this.conflictListeners.add(listener);
|
|
11
|
-
return () => this.conflictListeners.delete(listener);
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Subscribe to resolution events.
|
|
15
|
-
* @returns Unsubscribe function
|
|
16
|
-
*/
|
|
17
|
-
onResolution(listener) {
|
|
18
|
-
this.resolutionListeners.add(listener);
|
|
19
|
-
return () => this.resolutionListeners.delete(listener);
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Emit a conflict detection event (called by connector).
|
|
23
|
-
*/
|
|
24
|
-
emitConflict(conflict) {
|
|
25
|
-
this.conflictListeners.forEach((listener) => listener(conflict));
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Emit a resolution event (called by UI/ConflictContext).
|
|
29
|
-
*/
|
|
30
|
-
emitResolution(recordId, resolution) {
|
|
31
|
-
this.resolutionListeners.forEach((listener) => listener(recordId, resolution));
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Clear all listeners (for cleanup).
|
|
35
|
-
*/
|
|
36
|
-
destroy() {
|
|
37
|
-
this.conflictListeners.clear();
|
|
38
|
-
this.resolutionListeners.clear();
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
export {
|
|
43
|
-
ConflictBus
|
|
44
|
-
};
|
|
45
|
-
//# sourceMappingURL=chunk-42IJ25Q4.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/conflicts/conflict-bus.ts"],"sourcesContent":["import type { ConflictCheckResult, ConflictResolution } from './types';\n\nexport type ConflictListener = (conflict: ConflictCheckResult) => void;\nexport type ResolutionListener = (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(recordId: string, resolution: ConflictResolution): void {\n this.resolutionListeners.forEach((listener) => listener(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}\n"],"mappings":";AAcO,IAAM,cAAN,MAAkB;AAAA,EACf,oBAAoB,oBAAI,IAAsB;AAAA,EAC9C,sBAAsB,oBAAI,IAAwB;AAAA;AAAA;AAAA;AAAA;AAAA,EAM1D,WAAW,UAAwC;AACjD,SAAK,kBAAkB,IAAI,QAAQ;AACnC,WAAO,MAAM,KAAK,kBAAkB,OAAO,QAAQ;AAAA,EACrD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,aAAa,UAA0C;AACrD,SAAK,oBAAoB,IAAI,QAAQ;AACrC,WAAO,MAAM,KAAK,oBAAoB,OAAO,QAAQ;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,UAAqC;AAChD,SAAK,kBAAkB,QAAQ,CAAC,aAAa,SAAS,QAAQ,CAAC;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA,EAKA,eAAe,UAAkB,YAAsC;AACrE,SAAK,oBAAoB,QAAQ,CAAC,aAAa,SAAS,UAAU,UAAU,CAAC;AAAA,EAC/E;AAAA;AAAA;AAAA;AAAA,EAKA,UAAgB;AACd,SAAK,kBAAkB,MAAM;AAC7B,SAAK,oBAAoB,MAAM;AAAA,EACjC;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/platform/index.web.ts"],"sourcesContent":["/**\n * Web Platform Adapter for @pol-studios/powersync\n *\n * Implements the PlatformAdapter interface using Web APIs:\n * - @powersync/web for SQLite database (via wa-sqlite)\n * - IndexedDB for file storage (via idb-keyval pattern)\n * - localStorage for key-value storage\n * - navigator.onLine + events for network monitoring\n * - Canvas API for image compression\n */\n\nimport type {\n PlatformAdapter,\n DatabaseOptions,\n FileSystemAdapter,\n AsyncStorageAdapter,\n NetworkAdapter,\n LoggerAdapter,\n ImageProcessorAdapter,\n FileInfo,\n CompressedImage,\n CompressionOptions,\n ConnectionType,\n} from './types';\nimport type { AbstractPowerSyncDatabase } from '../core/types';\n\n// IndexedDB database name for file storage\nconst FILE_STORAGE_DB_NAME = 'powersync-files';\nconst FILE_STORAGE_STORE_NAME = 'files';\n\n/**\n * Simple IndexedDB wrapper for file storage\n */\nclass IndexedDBFileStorage {\n private dbPromise: Promise<IDBDatabase> | null = null;\n\n private getDB(): Promise<IDBDatabase> {\n if (!this.dbPromise) {\n this.dbPromise = new Promise((resolve, reject) => {\n const request = indexedDB.open(FILE_STORAGE_DB_NAME, 1);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result);\n\n request.onupgradeneeded = () => {\n const db = request.result;\n if (!db.objectStoreNames.contains(FILE_STORAGE_STORE_NAME)) {\n db.createObjectStore(FILE_STORAGE_STORE_NAME);\n }\n };\n });\n }\n return this.dbPromise;\n }\n\n async get(key: string): Promise<string | null> {\n const db = await this.getDB();\n return new Promise((resolve, reject) => {\n const transaction = db.transaction(FILE_STORAGE_STORE_NAME, 'readonly');\n const store = transaction.objectStore(FILE_STORAGE_STORE_NAME);\n const request = store.get(key);\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result ?? null);\n });\n }\n\n async set(key: string, value: string): Promise<void> {\n const db = await this.getDB();\n return new Promise((resolve, reject) => {\n const transaction = db.transaction(FILE_STORAGE_STORE_NAME, 'readwrite');\n const store = transaction.objectStore(FILE_STORAGE_STORE_NAME);\n const request = store.put(value, key);\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n }\n\n async delete(key: string): Promise<void> {\n const db = await this.getDB();\n return new Promise((resolve, reject) => {\n const transaction = db.transaction(FILE_STORAGE_STORE_NAME, 'readwrite');\n const store = transaction.objectStore(FILE_STORAGE_STORE_NAME);\n const request = store.delete(key);\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n }\n\n async has(key: string): Promise<boolean> {\n const db = await this.getDB();\n return new Promise((resolve, reject) => {\n const transaction = db.transaction(FILE_STORAGE_STORE_NAME, 'readonly');\n const store = transaction.objectStore(FILE_STORAGE_STORE_NAME);\n const request = store.count(key);\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result > 0);\n });\n }\n}\n\n/**\n * Create a Web platform adapter\n *\n * @param logger - Logger implementation to use\n * @returns Platform adapter configured for Web/PWA\n *\n * @example\n * ```typescript\n * import { createWebPlatformAdapter } from '@pol-studios/powersync/platform';\n *\n * const logger = {\n * debug: console.log,\n * info: console.log,\n * warn: console.warn,\n * error: console.error,\n * };\n *\n * const platform = createWebPlatformAdapter(logger);\n * ```\n */\nexport function createWebPlatformAdapter(\n logger: LoggerAdapter\n): PlatformAdapter {\n const fileStorage = new IndexedDBFileStorage();\n\n // Lazy load PowerSync web\n let PowerSyncDatabase: typeof import('@powersync/web').PowerSyncDatabase;\n let WASQLiteOpenFactory: typeof import('@powersync/web').WASQLiteOpenFactory;\n\n const loadPowerSync = async () => {\n if (!PowerSyncDatabase) {\n const psModule = await import('@powersync/web');\n PowerSyncDatabase = psModule.PowerSyncDatabase;\n WASQLiteOpenFactory = psModule.WASQLiteOpenFactory;\n }\n };\n\n // File system adapter implementation using IndexedDB\n const fileSystem: FileSystemAdapter = {\n async readFile(uri: string, encoding: 'base64' | 'utf8' = 'utf8'): Promise<string> {\n const content = await fileStorage.get(uri);\n if (content === null) {\n throw new Error(`File not found: ${uri}`);\n }\n return content;\n },\n\n async writeFile(\n uri: string,\n data: string,\n encoding: 'base64' | 'utf8' = 'utf8'\n ): Promise<void> {\n await fileStorage.set(uri, data);\n },\n\n async deleteFile(uri: string): Promise<void> {\n await fileStorage.delete(uri);\n },\n\n async copyFile(source: string, destination: string): Promise<void> {\n const content = await fileStorage.get(source);\n if (content === null) {\n throw new Error(`Source file not found: ${source}`);\n }\n await fileStorage.set(destination, content);\n },\n\n async moveFile(source: string, destination: string): Promise<void> {\n const content = await fileStorage.get(source);\n if (content === null) {\n throw new Error(`Source file not found: ${source}`);\n }\n await fileStorage.set(destination, content);\n await fileStorage.delete(source);\n },\n\n async getFileInfo(uri: string): Promise<FileInfo | null> {\n const exists = await fileStorage.has(uri);\n if (!exists) return null;\n\n const content = await fileStorage.get(uri);\n return {\n exists: true,\n size: content ? content.length : 0,\n isDirectory: false, // IndexedDB doesn't have directories\n };\n },\n\n async makeDirectory(\n uri: string,\n options?: { intermediates?: boolean }\n ): Promise<void> {\n // No-op for IndexedDB - directories are virtual\n },\n\n getDocumentsDirectory(): string {\n return '/powersync-docs/';\n },\n\n getCacheDirectory(): string {\n return '/powersync-cache/';\n },\n\n async getFreeDiskSpace(): Promise<number> {\n // Try to use Storage API if available\n if ('storage' in navigator && 'estimate' in navigator.storage) {\n try {\n const estimate = await navigator.storage.estimate();\n const quota = estimate.quota ?? 0;\n const usage = estimate.usage ?? 0;\n return quota - usage;\n } catch {\n // Fall through to default\n }\n }\n // Return a large default value if Storage API not available\n return 10 * 1024 * 1024 * 1024; // 10 GB default\n },\n };\n\n // Async storage adapter using localStorage\n const storage: AsyncStorageAdapter = {\n async getItem(key: string): Promise<string | null> {\n try {\n return localStorage.getItem(key);\n } catch {\n logger.warn('[Platform] localStorage.getItem failed for:', key);\n return null;\n }\n },\n\n async setItem(key: string, value: string): Promise<void> {\n try {\n localStorage.setItem(key, value);\n } catch (e) {\n logger.error('[Platform] localStorage.setItem failed:', e);\n throw e;\n }\n },\n\n async removeItem(key: string): Promise<void> {\n try {\n localStorage.removeItem(key);\n } catch {\n // Ignore removal errors\n }\n },\n\n async multiGet(keys: string[]): Promise<[string, string | null][]> {\n return keys.map((key) => [key, localStorage.getItem(key)]);\n },\n\n async multiSet(entries: [string, string][]): Promise<void> {\n for (const [key, value] of entries) {\n localStorage.setItem(key, value);\n }\n },\n };\n\n // Network adapter using navigator.onLine and events\n const network: NetworkAdapter = {\n async isConnected(): Promise<boolean> {\n return navigator.onLine;\n },\n\n async getConnectionType(): Promise<ConnectionType> {\n if (!navigator.onLine) return 'none';\n\n // Try to use Network Information API if available\n const nav = navigator as Navigator & {\n connection?: { effectiveType?: string; type?: string };\n };\n\n if (nav.connection) {\n const type = nav.connection.type;\n if (type === 'wifi') return 'wifi';\n if (type === 'cellular') return 'cellular';\n if (type === 'ethernet') return 'ethernet';\n }\n\n return 'unknown';\n },\n\n addConnectionListener(callback: (isConnected: boolean) => void): () => void {\n const handleOnline = () => callback(true);\n const handleOffline = () => callback(false);\n\n window.addEventListener('online', handleOnline);\n window.addEventListener('offline', handleOffline);\n\n return () => {\n window.removeEventListener('online', handleOnline);\n window.removeEventListener('offline', handleOffline);\n };\n },\n };\n\n // Image processor using Canvas API\n const imageProcessor: ImageProcessorAdapter = {\n async compress(\n uri: string,\n options: CompressionOptions\n ): Promise<CompressedImage> {\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.crossOrigin = 'anonymous';\n\n img.onload = () => {\n try {\n // Calculate target dimensions\n let width = img.width;\n let height = img.height;\n\n if (options.maxWidth && width > options.maxWidth) {\n height = (height * options.maxWidth) / width;\n width = options.maxWidth;\n }\n\n if (options.maxHeight && height > options.maxHeight) {\n width = (width * options.maxHeight) / height;\n height = options.maxHeight;\n }\n\n // Create canvas and draw image\n const canvas = document.createElement('canvas');\n canvas.width = Math.round(width);\n canvas.height = Math.round(height);\n\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n reject(new Error('Failed to get canvas context'));\n return;\n }\n\n ctx.drawImage(img, 0, 0, canvas.width, canvas.height);\n\n // Determine mime type\n let mimeType: string;\n switch (options.format) {\n case 'png':\n mimeType = 'image/png';\n break;\n case 'webp':\n mimeType = 'image/webp';\n break;\n case 'jpeg':\n default:\n mimeType = 'image/jpeg';\n break;\n }\n\n // Convert to data URL\n const dataUrl = canvas.toDataURL(mimeType, options.quality);\n\n resolve({\n uri: dataUrl,\n width: canvas.width,\n height: canvas.height,\n });\n } catch (e) {\n reject(e);\n }\n };\n\n img.onerror = () => {\n reject(new Error(`Failed to load image: ${uri}`));\n };\n\n img.src = uri;\n });\n },\n };\n\n // Main platform adapter\n return {\n async createDatabase(\n options: DatabaseOptions\n ): Promise<AbstractPowerSyncDatabase> {\n await loadPowerSync();\n\n logger.info('[Platform] Creating PowerSync web database:', options.dbFilename);\n\n const db = new PowerSyncDatabase!({\n schema: options.schema as any,\n database: new WASQLiteOpenFactory!({\n dbFilename: options.dbFilename,\n }),\n });\n\n logger.info('[Platform] Initializing database...');\n await db.init();\n\n // Verify database is queryable before returning\n // This prevents race conditions where db.connect() is called before SQLite is truly ready\n const maxAttempts = 3;\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n try {\n await db.get('SELECT 1');\n logger.info('[Platform] Database initialized and verified');\n return db as unknown as AbstractPowerSyncDatabase;\n } catch (err) {\n if (attempt < maxAttempts - 1) {\n logger.warn(`[Platform] Database readiness check failed (attempt ${attempt + 1}/${maxAttempts}), retrying...`);\n await new Promise(r => setTimeout(r, 100 * Math.pow(2, attempt))); // 100ms, 200ms, 400ms\n } else {\n logger.error('[Platform] Database failed readiness verification after all attempts');\n throw new Error(`Database failed readiness verification: ${err instanceof Error ? err.message : err}`);\n }\n }\n }\n\n // TypeScript: unreachable but needed for return type\n throw new Error('Database readiness verification failed');\n },\n\n fileSystem,\n storage,\n network,\n logger,\n imageProcessor,\n };\n}\n\n// Re-export types for convenience\nexport type { PlatformAdapter, LoggerAdapter } from './types';\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
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';\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/**\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","/**\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 {\n SyncStatusState,\n SyncStatusTrackerOptions,\n PowerSyncRawStatus,\n Unsubscribe,\n} 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\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\n constructor(\n storage: AsyncStorageAdapter,\n logger: LoggerAdapter,\n options: SyncStatusTrackerOptions = {}\n ) {\n this.storage = storage;\n this.logger = logger;\n this.notifyThrottleMs = options.notifyThrottleMs ?? STATUS_NOTIFY_THROTTLE_MS;\n this.onStatusChange = options.onStatusChange;\n\n this._state = {\n status: { ...DEFAULT_SYNC_STATUS },\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\n this._state = {\n status: newStatus,\n syncMode: this._state.syncMode,\n lastUpdated: new Date(),\n };\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\n if (this._state.syncMode === mode) return;\n\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\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(\n entries: CrudEntry[],\n error: SyncError,\n isPermanent: boolean,\n affectedEntityIds: string[],\n affectedTables: string[]\n ): 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\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\n this._failedTransactions.push(newFailure);\n\n // Enforce max stored failures (remove oldest)\n if (this._failedTransactions.length > this._maxStoredFailures) {\n this._failedTransactions.sort(\n (a, b) => a.firstFailedAt.getTime() - b.firstFailedAt.getTime()\n );\n this._failedTransactions = this._failedTransactions.slice(-this._maxStoredFailures);\n }\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\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\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 =>\n f.affectedEntityIds.includes(entityId)\n );\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\n this._failedTransactions = this._failedTransactions.filter(\n f => f.lastFailedAt.getTime() > cutoff\n );\n\n if (this._failedTransactions.length !== initialLength) {\n this.logger.debug(\n `[StatusTracker] Cleaned up ${initialLength - this._failedTransactions.length} stale failures`\n );\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\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\n this._notifyCompletedListeners();\n this.logger.debug(\n `[StatusTracker] Recorded completed transaction: ${completed.id} (${entries.length} entries)`\n );\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\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 (\n old.connected !== newStatus.connected ||\n old.connecting !== newStatus.connecting ||\n old.hasSynced !== newStatus.hasSynced ||\n old.uploading !== newStatus.uploading ||\n old.downloading !== newStatus.downloading ||\n old.lastSyncedAt?.getTime() !== newStatus.lastSyncedAt?.getTime() ||\n old.downloadProgress?.current !== newStatus.downloadProgress?.current ||\n old.downloadProgress?.target !== newStatus.downloadProgress?.target\n );\n }\n\n private _notifyListeners(forceImmediate = false): void {\n const now = Date.now();\n const timeSinceLastNotify = now - this._lastNotifyTime;\n\n if (this._notifyTimer) {\n clearTimeout(this._notifyTimer);\n this._notifyTimer = null;\n }\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\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\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\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","/**\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\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\n constructor(\n storage: AsyncStorageAdapter,\n logger: LoggerAdapter,\n options: MetricsCollectorOptions = {}\n ) {\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\n this._metrics = { ...DEFAULT_SYNC_METRICS };\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\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 = { ...DEFAULT_SYNC_METRICS, ...parsed };\n this.logger.debug('[MetricsCollector] Loaded persisted metrics');\n }\n } catch (err) {\n this.logger.warn('[MetricsCollector] Failed to load metrics:', err);\n }\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 { ...this._metrics };\n }\n\n // ─── Recording ─────────────────────────────────────────────────────────────\n\n /**\n * Record a completed sync operation.\n */\n async recordSync(data: SyncOperationData): Promise<void> {\n const { durationMs, success, operationsDownloaded, operationsUploaded, error } = data;\n\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 =\n (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, // Use original message as user message for metrics\n timestamp: new Date(),\n isPermanent: false,\n };\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\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, // Use original message as user message for metrics\n timestamp: new Date(),\n isPermanent: false,\n },\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\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 = { ...DEFAULT_SYNC_METRICS };\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\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\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","/**\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 {\n HEALTH_CHECK_INTERVAL_MS,\n HEALTH_CHECK_TIMEOUT_MS,\n LATENCY_DEGRADED_THRESHOLD_MS,\n MAX_CONSECUTIVE_FAILURES,\n} 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\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\n constructor(\n logger: LoggerAdapter,\n options: HealthMonitorOptions = {}\n ) {\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\n this._health = { ...DEFAULT_CONNECTION_HEALTH };\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\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\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\n this.logger.info('[HealthMonitor] Stopping');\n this._running = false;\n\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 { ...this._health };\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\n const startTime = Date.now();\n const timestamp = new Date();\n\n try {\n // Execute a simple query with timeout\n await this._withTimeout(\n this._db.get('SELECT 1'),\n this.checkTimeoutMs\n );\n\n const latencyMs = Date.now() - startTime;\n\n // Determine status based on latency\n const status: ConnectionHealth['status'] =\n latencyMs < this.degradedThresholdMs ? 'healthy' : 'degraded';\n\n this._updateHealth({\n status,\n latency: latencyMs,\n lastHealthCheck: timestamp,\n consecutiveFailures: 0,\n reconnectAttempts: this._health.reconnectAttempts,\n });\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\n const consecutiveFailures = this._health.consecutiveFailures + 1;\n const status: ConnectionHealth['status'] =\n consecutiveFailures >= this.maxConsecutiveFailures ? 'disconnected' : 'degraded';\n\n this._updateHealth({\n status,\n latency: null,\n lastHealthCheck: timestamp,\n consecutiveFailures,\n reconnectAttempts: this._health.reconnectAttempts,\n });\n\n return {\n success: false,\n error,\n timestamp,\n };\n }\n }\n\n private _updateHealth(health: ConnectionHealth): void {\n const changed = this._hasHealthChanged(health);\n this._health = health;\n\n if (changed) {\n this._notifyListeners();\n }\n }\n\n private _hasHealthChanged(newHealth: ConnectionHealth): boolean {\n const old = this._health;\n return (\n old.status !== newHealth.status ||\n old.latency !== newHealth.latency ||\n old.consecutiveFailures !== newHealth.consecutiveFailures ||\n old.reconnectAttempts !== newHealth.reconnectAttempts\n );\n }\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\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\n promise.then(\n (result) => {\n clearTimeout(timer);\n resolve(result);\n },\n (error) => {\n clearTimeout(timer);\n reject(error);\n }\n );\n });\n }\n}\n"],"mappings":"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","names":[]}
|