@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.
Files changed (46) hide show
  1. package/dist/attachments/index.js +1 -1
  2. package/dist/{chunk-PANEMMTU.js → chunk-3AYXHQ4W.js} +17 -11
  3. package/dist/chunk-3AYXHQ4W.js.map +1 -0
  4. package/dist/{chunk-BJ36QDFN.js → chunk-7EMDVIZX.js} +1 -1
  5. package/dist/chunk-7EMDVIZX.js.map +1 -0
  6. package/dist/{chunk-MB2RC3NS.js → chunk-C2RSTGDC.js} +129 -89
  7. package/dist/chunk-C2RSTGDC.js.map +1 -0
  8. package/dist/{chunk-NPNBGCRC.js → chunk-EJ23MXPQ.js} +1 -1
  9. package/dist/{chunk-NPNBGCRC.js.map → chunk-EJ23MXPQ.js.map} +1 -1
  10. package/dist/{chunk-CHRTN5PF.js → chunk-FPTDATY5.js} +1 -1
  11. package/dist/chunk-FPTDATY5.js.map +1 -0
  12. package/dist/chunk-GMFDCVMZ.js +1285 -0
  13. package/dist/chunk-GMFDCVMZ.js.map +1 -0
  14. package/dist/chunk-OLHGI472.js +1 -0
  15. package/dist/chunk-OLHGI472.js.map +1 -0
  16. package/dist/{chunk-CFCK2LHI.js → chunk-OTJXIRWX.js} +45 -40
  17. package/dist/chunk-OTJXIRWX.js.map +1 -0
  18. package/dist/{chunk-GBGATW2S.js → chunk-V6LJ6MR2.js} +86 -95
  19. package/dist/chunk-V6LJ6MR2.js.map +1 -0
  20. package/dist/connector/index.d.ts +1 -1
  21. package/dist/connector/index.js +2 -2
  22. package/dist/core/index.js +2 -2
  23. package/dist/{index-D952Qr38.d.ts → index-Cb-NI0Ct.d.ts} +9 -2
  24. package/dist/index.d.ts +2 -2
  25. package/dist/index.js +8 -9
  26. package/dist/index.native.d.ts +1 -1
  27. package/dist/index.native.js +9 -10
  28. package/dist/index.web.d.ts +1 -1
  29. package/dist/index.web.js +9 -10
  30. package/dist/platform/index.js.map +1 -1
  31. package/dist/platform/index.native.js +1 -1
  32. package/dist/platform/index.web.js +1 -1
  33. package/dist/provider/index.d.ts +6 -1
  34. package/dist/provider/index.js +6 -6
  35. package/dist/sync/index.js +3 -3
  36. package/package.json +3 -1
  37. package/dist/chunk-42IJ25Q4.js +0 -45
  38. package/dist/chunk-42IJ25Q4.js.map +0 -1
  39. package/dist/chunk-BJ36QDFN.js.map +0 -1
  40. package/dist/chunk-CFCK2LHI.js.map +0 -1
  41. package/dist/chunk-CHRTN5PF.js.map +0 -1
  42. package/dist/chunk-GBGATW2S.js.map +0 -1
  43. package/dist/chunk-H7HZMI4H.js +0 -925
  44. package/dist/chunk-H7HZMI4H.js.map +0 -1
  45. package/dist/chunk-MB2RC3NS.js.map +0 -1
  46. package/dist/chunk-PANEMMTU.js.map +0 -1
@@ -5,7 +5,7 @@ import {
5
5
  DEFAULT_CACHE_CONFIG,
6
6
  DEFAULT_COMPRESSION_CONFIG,
7
7
  DEFAULT_DOWNLOAD_CONFIG
8
- } from "../chunk-GBGATW2S.js";
8
+ } from "../chunk-V6LJ6MR2.js";
9
9
  export {
10
10
  AttachmentQueue,
11
11
  AttachmentState,
@@ -41,10 +41,10 @@ function createNativePlatformAdapter(logger) {
41
41
  if (!FileSystem) await loadDependencies();
42
42
  const parentDir = uri.substring(0, uri.lastIndexOf("/"));
43
43
  if (parentDir) {
44
- await FileSystem.makeDirectoryAsync(parentDir, { intermediates: true }).catch(
45
- () => {
46
- }
47
- );
44
+ await FileSystem.makeDirectoryAsync(parentDir, {
45
+ intermediates: true
46
+ }).catch(() => {
47
+ });
48
48
  }
49
49
  await FileSystem.writeAsStringAsync(uri, data, {
50
50
  encoding: encoding === "base64" ? FileSystem.EncodingType.Base64 : FileSystem.EncodingType.UTF8
@@ -52,15 +52,23 @@ function createNativePlatformAdapter(logger) {
52
52
  },
53
53
  async deleteFile(uri) {
54
54
  if (!FileSystem) await loadDependencies();
55
- await FileSystem.deleteAsync(uri, { idempotent: true });
55
+ await FileSystem.deleteAsync(uri, {
56
+ idempotent: true
57
+ });
56
58
  },
57
59
  async copyFile(source, destination) {
58
60
  if (!FileSystem) await loadDependencies();
59
- await FileSystem.copyAsync({ from: source, to: destination });
61
+ await FileSystem.copyAsync({
62
+ from: source,
63
+ to: destination
64
+ });
60
65
  },
61
66
  async moveFile(source, destination) {
62
67
  if (!FileSystem) await loadDependencies();
63
- await FileSystem.moveAsync({ from: source, to: destination });
68
+ await FileSystem.moveAsync({
69
+ from: source,
70
+ to: destination
71
+ });
64
72
  },
65
73
  async getFileInfo(uri) {
66
74
  if (!FileSystem) await loadDependencies();
@@ -135,9 +143,7 @@ function createNativePlatformAdapter(logger) {
135
143
  },
136
144
  addConnectionListener(callback) {
137
145
  if (!NetInfo) {
138
- logger.warn(
139
- "[Platform] NetInfo not loaded, connection listener may not work immediately"
140
- );
146
+ logger.warn("[Platform] NetInfo not loaded, connection listener may not work immediately");
141
147
  loadDependencies().then(() => {
142
148
  NetInfo.addEventListener((state) => {
143
149
  callback(state.isConnected ?? false);
@@ -229,4 +235,4 @@ function createNativePlatformAdapter(logger) {
229
235
  export {
230
236
  createNativePlatformAdapter
231
237
  };
232
- //# sourceMappingURL=chunk-PANEMMTU.js.map
238
+ //# sourceMappingURL=chunk-3AYXHQ4W.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/platform/index.native.ts"],"sourcesContent":["/**\n * React Native Platform Adapter for @pol-studios/powersync\n *\n * Implements the PlatformAdapter interface using React Native specific APIs:\n * - @powersync/react-native for SQLite database\n * - expo-file-system for file operations\n * - @react-native-async-storage/async-storage for key-value storage\n * - @react-native-community/netinfo for network monitoring\n * - expo-image-manipulator for image compression\n */\n\nimport type { PlatformAdapter, DatabaseOptions, FileSystemAdapter, AsyncStorageAdapter, NetworkAdapter, LoggerAdapter, ImageProcessorAdapter, FileInfo, CompressedImage, CompressionOptions, ConnectionType } from './types';\nimport type { AbstractPowerSyncDatabase } from '../core/types';\n\n/**\n * Create a React Native platform adapter\n *\n * @param logger - Logger implementation to use\n * @returns Platform adapter configured for React Native\n *\n * @example\n * ```typescript\n * import { createNativePlatformAdapter } 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 = createNativePlatformAdapter(logger);\n * ```\n */\nexport function createNativePlatformAdapter(logger: LoggerAdapter): PlatformAdapter {\n // Lazy imports to avoid loading these modules until needed\n // This also helps with tree-shaking in web builds\n let PowerSyncDatabase: typeof import('@powersync/react-native').PowerSyncDatabase;\n let OPSqliteOpenFactory: typeof import('@powersync/op-sqlite').OPSqliteOpenFactory;\n // Use 'any' for FileSystem to handle type changes between expo-file-system versions\n let FileSystem: any;\n let AsyncStorage: typeof import('@react-native-async-storage/async-storage').default;\n let NetInfo: typeof import('@react-native-community/netinfo').default;\n let ImageManipulator: typeof import('expo-image-manipulator');\n const loadDependencies = async () => {\n if (!PowerSyncDatabase) {\n const psModule = await import('@powersync/react-native');\n PowerSyncDatabase = psModule.PowerSyncDatabase;\n }\n if (!OPSqliteOpenFactory) {\n const opSqliteModule = await import('@powersync/op-sqlite');\n OPSqliteOpenFactory = opSqliteModule.OPSqliteOpenFactory;\n }\n if (!FileSystem) {\n FileSystem = await import('expo-file-system');\n }\n if (!AsyncStorage) {\n const asModule = await import('@react-native-async-storage/async-storage');\n AsyncStorage = asModule.default;\n }\n if (!NetInfo) {\n const niModule = await import('@react-native-community/netinfo');\n NetInfo = niModule.default;\n }\n if (!ImageManipulator) {\n ImageManipulator = await import('expo-image-manipulator');\n }\n };\n\n // File system adapter implementation\n const fileSystem: FileSystemAdapter = {\n async readFile(uri: string, encoding: 'base64' | 'utf8' = 'utf8'): Promise<string> {\n if (!FileSystem) await loadDependencies();\n return FileSystem!.readAsStringAsync(uri, {\n encoding: encoding === 'base64' ? FileSystem!.EncodingType.Base64 : FileSystem!.EncodingType.UTF8\n });\n },\n async writeFile(uri: string, data: string, encoding: 'base64' | 'utf8' = 'utf8'): Promise<void> {\n if (!FileSystem) await loadDependencies();\n // Ensure parent directory exists\n const parentDir = uri.substring(0, uri.lastIndexOf('/'));\n if (parentDir) {\n await FileSystem!.makeDirectoryAsync(parentDir, {\n intermediates: true\n }).catch(() => {\n // Directory may already exist\n });\n }\n await FileSystem!.writeAsStringAsync(uri, data, {\n encoding: encoding === 'base64' ? FileSystem!.EncodingType.Base64 : FileSystem!.EncodingType.UTF8\n });\n },\n async deleteFile(uri: string): Promise<void> {\n if (!FileSystem) await loadDependencies();\n await FileSystem!.deleteAsync(uri, {\n idempotent: true\n });\n },\n async copyFile(source: string, destination: string): Promise<void> {\n if (!FileSystem) await loadDependencies();\n await FileSystem!.copyAsync({\n from: source,\n to: destination\n });\n },\n async moveFile(source: string, destination: string): Promise<void> {\n if (!FileSystem) await loadDependencies();\n await FileSystem!.moveAsync({\n from: source,\n to: destination\n });\n },\n async getFileInfo(uri: string): Promise<FileInfo | null> {\n if (!FileSystem) await loadDependencies();\n try {\n const info = await FileSystem!.getInfoAsync(uri);\n if (!info.exists) return null;\n return {\n exists: true,\n size: 'size' in info ? info.size ?? 0 : 0,\n isDirectory: info.isDirectory ?? false,\n modificationTime: 'modificationTime' in info && info.modificationTime ? new Date(info.modificationTime * 1000) : undefined\n };\n } catch {\n return null;\n }\n },\n async makeDirectory(uri: string, options?: {\n intermediates?: boolean;\n }): Promise<void> {\n if (!FileSystem) await loadDependencies();\n await FileSystem!.makeDirectoryAsync(uri, {\n intermediates: options?.intermediates ?? true\n });\n },\n getDocumentsDirectory(): string {\n // Note: This will be set after first load\n // Return a placeholder that will work for path construction\n return FileSystem?.documentDirectory ?? '';\n },\n getCacheDirectory(): string {\n return FileSystem?.cacheDirectory ?? '';\n },\n async getFreeDiskSpace(): Promise<number> {\n if (!FileSystem) await loadDependencies();\n return FileSystem!.getFreeDiskStorageAsync();\n }\n };\n\n // Async storage adapter implementation\n const storage: AsyncStorageAdapter = {\n async getItem(key: string): Promise<string | null> {\n if (!AsyncStorage) await loadDependencies();\n return AsyncStorage!.getItem(key);\n },\n async setItem(key: string, value: string): Promise<void> {\n if (!AsyncStorage) await loadDependencies();\n await AsyncStorage!.setItem(key, value);\n },\n async removeItem(key: string): Promise<void> {\n if (!AsyncStorage) await loadDependencies();\n await AsyncStorage!.removeItem(key);\n },\n async multiGet(keys: string[]): Promise<[string, string | null][]> {\n if (!AsyncStorage) await loadDependencies();\n const result = await AsyncStorage!.multiGet(keys);\n return result as [string, string | null][];\n },\n async multiSet(entries: [string, string][]): Promise<void> {\n if (!AsyncStorage) await loadDependencies();\n await AsyncStorage!.multiSet(entries);\n }\n };\n\n // Network adapter implementation\n const network: NetworkAdapter = {\n async isConnected(): Promise<boolean> {\n if (!NetInfo) await loadDependencies();\n const state = await NetInfo!.fetch();\n return state.isConnected ?? false;\n },\n async getConnectionType(): Promise<ConnectionType> {\n if (!NetInfo) await loadDependencies();\n const state = await NetInfo!.fetch();\n const type = state.type;\n if (type === 'wifi') return 'wifi';\n if (type === 'cellular') return 'cellular';\n if (type === 'ethernet') return 'ethernet';\n if (type === 'none') return 'none';\n return 'unknown';\n },\n addConnectionListener(callback: (isConnected: boolean) => void): () => void {\n // NetInfo must be loaded synchronously for listener setup\n // This is typically called after initialization\n if (!NetInfo) {\n logger.warn('[Platform] NetInfo not loaded, connection listener may not work immediately');\n // Load and set up listener asynchronously\n loadDependencies().then(() => {\n NetInfo!.addEventListener(state => {\n callback(state.isConnected ?? false);\n });\n });\n return () => {};\n }\n const unsubscribe = NetInfo.addEventListener(state => {\n callback(state.isConnected ?? false);\n });\n return unsubscribe;\n }\n };\n\n // Image processor adapter implementation\n const imageProcessor: ImageProcessorAdapter = {\n async compress(uri: string, options: CompressionOptions): Promise<CompressedImage> {\n if (!ImageManipulator) await loadDependencies();\n const actions: import('expo-image-manipulator').Action[] = [];\n\n // Add resize action if maxWidth or maxHeight specified\n if (options.maxWidth || options.maxHeight) {\n actions.push({\n resize: {\n width: options.maxWidth,\n height: options.maxHeight\n }\n });\n }\n\n // Determine output format\n let format: import('expo-image-manipulator').SaveFormat;\n switch (options.format) {\n case 'png':\n format = ImageManipulator!.SaveFormat.PNG;\n break;\n case 'webp':\n format = ImageManipulator!.SaveFormat.WEBP;\n break;\n case 'jpeg':\n default:\n format = ImageManipulator!.SaveFormat.JPEG;\n break;\n }\n const result = await ImageManipulator!.manipulateAsync(uri, actions, {\n compress: options.quality,\n format\n });\n return {\n uri: result.uri,\n width: result.width,\n height: result.height\n };\n }\n };\n\n // Main platform adapter\n return {\n async createDatabase(options: DatabaseOptions): Promise<AbstractPowerSyncDatabase> {\n if (!PowerSyncDatabase) await loadDependencies();\n logger.info('[Platform] Creating PowerSync database:', options.dbFilename);\n const db = new PowerSyncDatabase!({\n schema: options.schema as any,\n database: new OPSqliteOpenFactory!({\n dbFilename: options.dbFilename\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 fileSystem,\n storage,\n network,\n logger,\n imageProcessor\n };\n}\n\n// Re-export types for convenience\nexport type { PlatformAdapter, LoggerAdapter } from './types';"],"mappings":"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","names":[]}
@@ -287,4 +287,4 @@ function createWebPlatformAdapter(logger) {
287
287
  export {
288
288
  createWebPlatformAdapter
289
289
  };
290
- //# sourceMappingURL=chunk-BJ36QDFN.js.map
290
+ //# sourceMappingURL=chunk-7EMDVIZX.js.map
@@ -0,0 +1 @@
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 { PlatformAdapter, DatabaseOptions, FileSystemAdapter, AsyncStorageAdapter, NetworkAdapter, LoggerAdapter, ImageProcessorAdapter, FileInfo, CompressedImage, CompressionOptions, ConnectionType } 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 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 request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result);\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 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 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 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 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(logger: LoggerAdapter): 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 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 async writeFile(uri: string, data: string, encoding: 'base64' | 'utf8' = 'utf8'): Promise<void> {\n await fileStorage.set(uri, data);\n },\n async deleteFile(uri: string): Promise<void> {\n await fileStorage.delete(uri);\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 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 async getFileInfo(uri: string): Promise<FileInfo | null> {\n const exists = await fileStorage.has(uri);\n if (!exists) return null;\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 async makeDirectory(uri: string, options?: {\n intermediates?: boolean;\n }): Promise<void> {\n // No-op for IndexedDB - directories are virtual\n },\n getDocumentsDirectory(): string {\n return '/powersync-docs/';\n },\n getCacheDirectory(): string {\n return '/powersync-cache/';\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 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 async removeItem(key: string): Promise<void> {\n try {\n localStorage.removeItem(key);\n } catch {\n // Ignore removal errors\n }\n },\n async multiGet(keys: string[]): Promise<[string, string | null][]> {\n return keys.map(key => [key, localStorage.getItem(key)]);\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 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?: {\n effectiveType?: string;\n 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 return 'unknown';\n },\n addConnectionListener(callback: (isConnected: boolean) => void): () => void {\n const handleOnline = () => callback(true);\n const handleOffline = () => callback(false);\n window.addEventListener('online', handleOnline);\n window.addEventListener('offline', handleOffline);\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(uri: string, options: CompressionOptions): Promise<CompressedImage> {\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.crossOrigin = 'anonymous';\n img.onload = () => {\n try {\n // Calculate target dimensions\n let width = img.width;\n let height = img.height;\n if (options.maxWidth && width > options.maxWidth) {\n height = height * options.maxWidth / width;\n width = options.maxWidth;\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 const ctx = canvas.getContext('2d');\n if (!ctx) {\n reject(new Error('Failed to get canvas context'));\n return;\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 resolve({\n uri: dataUrl,\n width: canvas.width,\n height: canvas.height\n });\n } catch (e) {\n reject(e);\n }\n };\n img.onerror = () => {\n reject(new Error(`Failed to load image: ${uri}`));\n };\n img.src = uri;\n });\n }\n };\n\n // Main platform adapter\n return {\n async createDatabase(options: DatabaseOptions): Promise<AbstractPowerSyncDatabase> {\n await loadPowerSync();\n logger.info('[Platform] Creating PowerSync web database:', options.dbFilename);\n const db = new PowerSyncDatabase!({\n schema: options.schema as any,\n database: new WASQLiteOpenFactory!({\n dbFilename: options.dbFilename\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 fileSystem,\n storage,\n network,\n logger,\n imageProcessor\n };\n}\n\n// Re-export types for convenience\nexport type { PlatformAdapter, LoggerAdapter } from './types';"],"mappings":"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","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  classifySupabaseError
3
- } from "./chunk-CHRTN5PF.js";
3
+ } from "./chunk-FPTDATY5.js";
4
4
 
5
5
  // src/connector/types.ts
6
6
  var defaultSchemaRouter = () => "public";
@@ -14,10 +14,7 @@ function validateTableName(table) {
14
14
  }
15
15
  var DEFAULT_IGNORED_FIELDS = ["updatedAt", "createdAt", "_version", "id"];
16
16
  async function detectConflicts(table, recordId, localVersion, serverVersion, pendingChanges, supabase, config) {
17
- const ignoredFields = /* @__PURE__ */ new Set([
18
- ...DEFAULT_IGNORED_FIELDS,
19
- ...config?.ignoredFields ?? []
20
- ]);
17
+ const ignoredFields = /* @__PURE__ */ new Set([...DEFAULT_IGNORED_FIELDS, ...config?.ignoredFields ?? []]);
21
18
  const filteredPendingChanges = {};
22
19
  for (const [field, value] of Object.entries(pendingChanges)) {
23
20
  if (!ignoredFields.has(field)) {
@@ -33,7 +30,12 @@ async function detectConflicts(table, recordId, localVersion, serverVersion, pen
33
30
  recordId
34
31
  };
35
32
  }
36
- const { data: auditLogs, error } = await supabase.schema("core").from("AuditLog").select("oldRecord, newRecord, changeBy, changeAt").eq("tableName", table).eq("recordId_text", recordId).order("changeAt", { ascending: false }).limit(20);
33
+ const {
34
+ data: auditLogs,
35
+ error
36
+ } = await supabase.schema("core").from("AuditLog").select("oldRecord, newRecord, changeBy, changeAt").eq("tableName", table).eq("recordId_text", recordId).order("changeAt", {
37
+ ascending: false
38
+ }).limit(20);
37
39
  if (error) {
38
40
  console.warn("[detectConflicts] Failed to query AuditLog:", error);
39
41
  return {
@@ -87,9 +89,7 @@ async function detectConflicts(table, recordId, localVersion, serverVersion, pen
87
89
  async function hasVersionColumn(table, db) {
88
90
  try {
89
91
  validateTableName(table);
90
- const result = await db.getAll(
91
- `PRAGMA table_info("${table}")`
92
- );
92
+ const result = await db.getAll(`PRAGMA table_info("${table}")`);
93
93
  return result.some((col) => col.name === "_version");
94
94
  } catch {
95
95
  return false;
@@ -97,7 +97,10 @@ async function hasVersionColumn(table, db) {
97
97
  }
98
98
  async function fetchServerVersion(table, recordId, schema, supabase) {
99
99
  const query = schema === "public" ? supabase.from(table) : supabase.schema(schema).from(table);
100
- const { data, error } = await query.select("_version").eq("id", recordId).single();
100
+ const {
101
+ data,
102
+ error
103
+ } = await query.select("_version").eq("id", recordId).single();
101
104
  if (error || !data) {
102
105
  return null;
103
106
  }
@@ -105,10 +108,7 @@ async function fetchServerVersion(table, recordId, schema, supabase) {
105
108
  }
106
109
  async function getLocalVersion(table, recordId, db) {
107
110
  validateTableName(table);
108
- const result = await db.get(
109
- `SELECT _version FROM "${table}" WHERE id = ?`,
110
- [recordId]
111
- );
111
+ const result = await db.get(`SELECT _version FROM "${table}" WHERE id = ?`, [recordId]);
112
112
  return result?._version ?? null;
113
113
  }
114
114
 
@@ -131,6 +131,11 @@ var SupabaseConnector = class {
131
131
  versionColumnCache = /* @__PURE__ */ new Map();
132
132
  // Active project IDs for scoped sync (optional feature)
133
133
  activeProjectIds = [];
134
+ // Store resolutions for retry - when PowerSync retries, we apply stored resolutions
135
+ // instead of re-detecting conflicts that the user already resolved
136
+ resolvedConflicts = /* @__PURE__ */ new Map();
137
+ // Cleanup function for resolution listener subscription
138
+ unsubscribeResolution;
134
139
  constructor(options) {
135
140
  this.supabase = options.supabaseClient;
136
141
  this.powerSyncUrl = options.powerSyncUrl;
@@ -144,6 +149,31 @@ var SupabaseConnector = class {
144
149
  this.conflictDetection = options.conflictDetection;
145
150
  this.conflictHandler = options.conflictHandler;
146
151
  this.conflictBus = options.conflictBus;
152
+ if (this.conflictBus) {
153
+ this.unsubscribeResolution = this.conflictBus.onResolution((table, recordId, resolution) => {
154
+ const key = `${table}:${recordId}`;
155
+ if (__DEV__) {
156
+ console.log("[Connector] Storing resolution for retry:", {
157
+ table,
158
+ recordId,
159
+ key,
160
+ resolution
161
+ });
162
+ }
163
+ this.resolvedConflicts.set(key, resolution);
164
+ });
165
+ }
166
+ }
167
+ /**
168
+ * Clean up resources (unsubscribe from event listeners).
169
+ * Call this when the connector is no longer needed.
170
+ */
171
+ destroy() {
172
+ if (this.unsubscribeResolution) {
173
+ this.unsubscribeResolution();
174
+ this.unsubscribeResolution = void 0;
175
+ }
176
+ this.resolvedConflicts.clear();
147
177
  }
148
178
  /**
149
179
  * Set the active project IDs for scoped sync.
@@ -168,7 +198,9 @@ var SupabaseConnector = class {
168
198
  async fetchCredentials() {
169
199
  this.logger?.debug("[Connector] Fetching credentials...");
170
200
  const {
171
- data: { session },
201
+ data: {
202
+ session
203
+ },
172
204
  error
173
205
  } = await this.supabase.auth.getSession();
174
206
  if (error) {
@@ -179,10 +211,7 @@ var SupabaseConnector = class {
179
211
  this.logger?.error("[Connector] No active session");
180
212
  throw new Error("No active Supabase session");
181
213
  }
182
- this.logger?.debug(
183
- "[Connector] Credentials fetched, token expires at:",
184
- session.expires_at
185
- );
214
+ this.logger?.debug("[Connector] Credentials fetched, token expires at:", session.expires_at);
186
215
  return {
187
216
  endpoint: this.powerSyncUrl,
188
217
  token: session.access_token,
@@ -234,7 +263,9 @@ var SupabaseConnector = class {
234
263
  await this.processTransaction(transaction, database);
235
264
  return;
236
265
  }
237
- const { crud } = transaction;
266
+ const {
267
+ crud
268
+ } = transaction;
238
269
  const skipTables = new Set(this.conflictDetection?.skipTables ?? []);
239
270
  const entriesToProcess = [];
240
271
  const entriesQueuedForUI = [];
@@ -249,6 +280,35 @@ var SupabaseConnector = class {
249
280
  entriesToProcess.push(entry);
250
281
  continue;
251
282
  }
283
+ const resolutionKey = `${entry.table}:${entry.id}`;
284
+ const existingResolution = this.resolvedConflicts.get(resolutionKey);
285
+ if (existingResolution) {
286
+ if (__DEV__) {
287
+ console.log("[Connector] Applying stored resolution for retry:", {
288
+ table: entry.table,
289
+ id: entry.id,
290
+ key: resolutionKey,
291
+ resolution: existingResolution
292
+ });
293
+ }
294
+ this.resolvedConflicts.delete(resolutionKey);
295
+ switch (existingResolution.action) {
296
+ case "overwrite":
297
+ entriesToProcess.push(entry);
298
+ break;
299
+ case "keep-server":
300
+ entriesDiscarded.push(entry);
301
+ break;
302
+ case "partial":
303
+ const partialEntry = {
304
+ ...entry,
305
+ opData: this.filterFields(entry.opData ?? {}, existingResolution.fields)
306
+ };
307
+ entriesToProcess.push(partialEntry);
308
+ break;
309
+ }
310
+ continue;
311
+ }
252
312
  const hasVersion = await this.checkVersionColumn(entry.table, database);
253
313
  if (!hasVersion) {
254
314
  entriesToProcess.push(entry);
@@ -256,25 +316,12 @@ var SupabaseConnector = class {
256
316
  }
257
317
  const localVersion = await getLocalVersion(entry.table, entry.id, database);
258
318
  const schema = this.schemaRouter(entry.table);
259
- const serverVersion = await fetchServerVersion(
260
- entry.table,
261
- entry.id,
262
- schema,
263
- this.supabase
264
- );
319
+ const serverVersion = await fetchServerVersion(entry.table, entry.id, schema, this.supabase);
265
320
  if (localVersion === null || serverVersion === null) {
266
321
  entriesToProcess.push(entry);
267
322
  continue;
268
323
  }
269
- const conflictResult = await detectConflicts(
270
- entry.table,
271
- entry.id,
272
- localVersion,
273
- serverVersion,
274
- entry.opData ?? {},
275
- this.supabase,
276
- this.conflictDetection
277
- );
324
+ const conflictResult = await detectConflicts(entry.table, entry.id, localVersion, serverVersion, entry.opData ?? {}, this.supabase, this.conflictDetection);
278
325
  if (!conflictResult.hasConflict) {
279
326
  entriesToProcess.push(entry);
280
327
  continue;
@@ -348,11 +395,7 @@ var SupabaseConnector = class {
348
395
  this.onTransactionComplete?.(entriesDiscarded);
349
396
  } catch (error) {
350
397
  const classified = classifySupabaseError(error);
351
- this.onTransactionFailure?.(
352
- entriesDiscarded,
353
- error instanceof Error ? error : new Error(String(error)),
354
- classified
355
- );
398
+ this.onTransactionFailure?.(entriesDiscarded, error instanceof Error ? error : new Error(String(error)), classified);
356
399
  throw error;
357
400
  }
358
401
  return;
@@ -380,11 +423,12 @@ var SupabaseConnector = class {
380
423
  });
381
424
  }
382
425
  if (this.conflictBus && partialResolutions.length > 0) {
383
- for (const { originalConflict, syncedFields } of partialResolutions) {
426
+ for (const {
427
+ originalConflict,
428
+ syncedFields
429
+ } of partialResolutions) {
384
430
  const syncedFieldSet = new Set(syncedFields);
385
- const remainingConflicts = originalConflict.conflicts.filter(
386
- (c) => !syncedFieldSet.has(c.field)
387
- );
431
+ const remainingConflicts = originalConflict.conflicts.filter((c) => !syncedFieldSet.has(c.field));
388
432
  if (remainingConflicts.length > 0) {
389
433
  if (__DEV__) {
390
434
  console.log("[Connector] Re-emitting conflict for remaining fields:", {
@@ -423,13 +467,13 @@ var SupabaseConnector = class {
423
467
  this.logger?.error("[Connector] Upload error:", {
424
468
  error,
425
469
  classified,
426
- entries: entriesToProcess.map((e) => ({ table: e.table, op: e.op, id: e.id }))
470
+ entries: entriesToProcess.map((e) => ({
471
+ table: e.table,
472
+ op: e.op,
473
+ id: e.id
474
+ }))
427
475
  });
428
- this.onTransactionFailure?.(
429
- entriesToProcess,
430
- error instanceof Error ? error : new Error(String(error)),
431
- classified
432
- );
476
+ this.onTransactionFailure?.(entriesToProcess, error instanceof Error ? error : new Error(String(error)), classified);
433
477
  throw error;
434
478
  }
435
479
  }
@@ -478,13 +522,13 @@ var SupabaseConnector = class {
478
522
  this.logger?.error("[Connector] Upload error:", {
479
523
  error,
480
524
  classified,
481
- entries: transaction.crud.map((e) => ({ table: e.table, op: e.op, id: e.id }))
525
+ entries: transaction.crud.map((e) => ({
526
+ table: e.table,
527
+ op: e.op,
528
+ id: e.id
529
+ }))
482
530
  });
483
- this.onTransactionFailure?.(
484
- transaction.crud,
485
- error instanceof Error ? error : new Error(String(error)),
486
- classified
487
- );
531
+ this.onTransactionFailure?.(transaction.crud, error instanceof Error ? error : new Error(String(error)), classified);
488
532
  throw error;
489
533
  }
490
534
  }
@@ -535,24 +579,14 @@ var SupabaseConnector = class {
535
579
  handled = await this.crudHandler.handlePut?.(entry, this.supabase, schema) ?? false;
536
580
  break;
537
581
  case "PATCH" /* PATCH */:
538
- handled = await this.crudHandler.handlePatch?.(
539
- entry,
540
- this.supabase,
541
- schema
542
- ) ?? false;
582
+ handled = await this.crudHandler.handlePatch?.(entry, this.supabase, schema) ?? false;
543
583
  break;
544
584
  case "DELETE" /* DELETE */:
545
- handled = await this.crudHandler.handleDelete?.(
546
- entry,
547
- this.supabase,
548
- schema
549
- ) ?? false;
585
+ handled = await this.crudHandler.handleDelete?.(entry, this.supabase, schema) ?? false;
550
586
  break;
551
587
  }
552
588
  if (handled) {
553
- this.logger?.debug(
554
- `[Connector] Custom handler processed ${entry.op} for ${schema}.${table}`
555
- );
589
+ this.logger?.debug(`[Connector] Custom handler processed ${entry.op} for ${schema}.${table}`);
556
590
  return;
557
591
  }
558
592
  }
@@ -564,13 +598,21 @@ var SupabaseConnector = class {
564
598
  schema,
565
599
  table,
566
600
  id,
567
- data: { id, ...entry.opData }
601
+ data: {
602
+ id,
603
+ ...entry.opData
604
+ }
568
605
  });
569
606
  }
570
- const { data: upsertData, error: upsertError } = await query.upsert(
571
- { id, ...entry.opData },
572
- { onConflict: "id" }
573
- ).select();
607
+ const {
608
+ data: upsertData,
609
+ error: upsertError
610
+ } = await query.upsert({
611
+ id,
612
+ ...entry.opData
613
+ }, {
614
+ onConflict: "id"
615
+ }).select();
574
616
  if (upsertError) {
575
617
  if (__DEV__) {
576
618
  console.error("[Connector] PUT/UPSERT FAILED:", {
@@ -584,9 +626,7 @@ var SupabaseConnector = class {
584
626
  errorHint: upsertError.hint
585
627
  });
586
628
  }
587
- throw new Error(
588
- `Upsert failed for ${schema}.${table}: ${upsertError.message}`
589
- );
629
+ throw new Error(`Upsert failed for ${schema}.${table}: ${upsertError.message}`);
590
630
  }
591
631
  if (__DEV__) {
592
632
  console.log("[Connector] PUT/UPSERT SUCCESS:", {
@@ -606,7 +646,10 @@ var SupabaseConnector = class {
606
646
  opData: entry.opData
607
647
  });
608
648
  }
609
- const { data: updateData, error: updateError } = await query.update(entry.opData).eq("id", id).select();
649
+ const {
650
+ data: updateData,
651
+ error: updateError
652
+ } = await query.update(entry.opData).eq("id", id).select();
610
653
  if (updateError) {
611
654
  if (__DEV__) {
612
655
  console.error("[Connector] PATCH/UPDATE FAILED:", {
@@ -620,9 +663,7 @@ var SupabaseConnector = class {
620
663
  errorHint: updateError.hint
621
664
  });
622
665
  }
623
- throw new Error(
624
- `Update failed for ${schema}.${table}: ${updateError.message}`
625
- );
666
+ throw new Error(`Update failed for ${schema}.${table}: ${updateError.message}`);
626
667
  }
627
668
  if (__DEV__) {
628
669
  console.log("[Connector] PATCH/UPDATE SUCCESS:", {
@@ -641,7 +682,10 @@ var SupabaseConnector = class {
641
682
  id
642
683
  });
643
684
  }
644
- const { data: deleteData, error: deleteError } = await query.delete().eq("id", id).select();
685
+ const {
686
+ data: deleteData,
687
+ error: deleteError
688
+ } = await query.delete().eq("id", id).select();
645
689
  if (deleteError) {
646
690
  if (__DEV__) {
647
691
  console.error("[Connector] DELETE FAILED:", {
@@ -655,9 +699,7 @@ var SupabaseConnector = class {
655
699
  errorHint: deleteError.hint
656
700
  });
657
701
  }
658
- throw new Error(
659
- `Delete failed for ${schema}.${table}: ${deleteError.message}`
660
- );
702
+ throw new Error(`Delete failed for ${schema}.${table}: ${deleteError.message}`);
661
703
  }
662
704
  if (__DEV__) {
663
705
  console.log("[Connector] DELETE SUCCESS:", {
@@ -669,9 +711,7 @@ var SupabaseConnector = class {
669
711
  }
670
712
  break;
671
713
  }
672
- this.logger?.debug(
673
- `[Connector] Processed ${entry.op} for ${schema}.${table} (id: ${id})`
674
- );
714
+ this.logger?.debug(`[Connector] Processed ${entry.op} for ${schema}.${table} (id: ${id})`);
675
715
  }
676
716
  };
677
717
 
@@ -683,4 +723,4 @@ export {
683
723
  getLocalVersion,
684
724
  SupabaseConnector
685
725
  };
686
- //# sourceMappingURL=chunk-MB2RC3NS.js.map
726
+ //# sourceMappingURL=chunk-C2RSTGDC.js.map