@pol-studios/powersync 1.0.6 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/dist/CacheSettingsManager-1exbOC6S.d.ts +261 -0
  2. package/dist/attachments/index.d.ts +65 -355
  3. package/dist/attachments/index.js +24 -6
  4. package/dist/{types-Cd7RhNqf.d.ts → background-sync-ChCXW-EV.d.ts} +53 -2
  5. package/dist/chunk-4C3RY5SU.js +204 -0
  6. package/dist/chunk-4C3RY5SU.js.map +1 -0
  7. package/dist/{chunk-3AYXHQ4W.js → chunk-53WH2JJV.js} +111 -47
  8. package/dist/chunk-53WH2JJV.js.map +1 -0
  9. package/dist/chunk-A4IBBWGO.js +377 -0
  10. package/dist/chunk-A4IBBWGO.js.map +1 -0
  11. package/dist/chunk-BREGB4WL.js +1768 -0
  12. package/dist/chunk-BREGB4WL.js.map +1 -0
  13. package/dist/{chunk-EJ23MXPQ.js → chunk-CGL33PL4.js} +3 -1
  14. package/dist/chunk-CGL33PL4.js.map +1 -0
  15. package/dist/chunk-DGUM43GV.js +11 -0
  16. package/dist/chunk-DHYUBVP7.js +131 -0
  17. package/dist/chunk-DHYUBVP7.js.map +1 -0
  18. package/dist/chunk-FV2HXEIY.js +124 -0
  19. package/dist/chunk-FV2HXEIY.js.map +1 -0
  20. package/dist/chunk-GKF7TOMT.js +1 -0
  21. package/dist/{chunk-R4YFWQ3Q.js → chunk-H772V6XQ.js} +304 -51
  22. package/dist/chunk-H772V6XQ.js.map +1 -0
  23. package/dist/{chunk-62J2DPKX.js → chunk-HFOFLW5F.js} +396 -412
  24. package/dist/chunk-HFOFLW5F.js.map +1 -0
  25. package/dist/chunk-KGSFAE5B.js +1 -0
  26. package/dist/chunk-LNL64IJZ.js +1 -0
  27. package/dist/chunk-MKD2VCX3.js +32 -0
  28. package/dist/chunk-MKD2VCX3.js.map +1 -0
  29. package/dist/{chunk-7EMDVIZX.js → chunk-N75DEF5J.js} +19 -1
  30. package/dist/chunk-N75DEF5J.js.map +1 -0
  31. package/dist/chunk-P6WOZO7H.js +49 -0
  32. package/dist/chunk-P6WOZO7H.js.map +1 -0
  33. package/dist/chunk-TGBT5XBE.js +1 -0
  34. package/dist/chunk-TGBT5XBE.js.map +1 -0
  35. package/dist/chunk-UEYRTLKE.js +72 -0
  36. package/dist/chunk-UEYRTLKE.js.map +1 -0
  37. package/dist/chunk-WGHNIAF7.js +329 -0
  38. package/dist/chunk-WGHNIAF7.js.map +1 -0
  39. package/dist/chunk-WQ5MPAVC.js +449 -0
  40. package/dist/chunk-WQ5MPAVC.js.map +1 -0
  41. package/dist/{chunk-FPTDATY5.js → chunk-XQAJM2MW.js} +22 -11
  42. package/dist/chunk-XQAJM2MW.js.map +1 -0
  43. package/dist/chunk-YSTEESEG.js +676 -0
  44. package/dist/chunk-YSTEESEG.js.map +1 -0
  45. package/dist/chunk-ZEOKPWUC.js +1165 -0
  46. package/dist/chunk-ZEOKPWUC.js.map +1 -0
  47. package/dist/connector/index.d.ts +182 -3
  48. package/dist/connector/index.js +12 -4
  49. package/dist/core/index.d.ts +5 -3
  50. package/dist/core/index.js +5 -2
  51. package/dist/error/index.d.ts +54 -0
  52. package/dist/error/index.js +8 -0
  53. package/dist/error/index.js.map +1 -0
  54. package/dist/index.d.ts +100 -12
  55. package/dist/index.js +148 -38
  56. package/dist/index.native.d.ts +20 -10
  57. package/dist/index.native.js +148 -39
  58. package/dist/index.web.d.ts +20 -10
  59. package/dist/index.web.js +149 -39
  60. package/dist/maintenance/index.d.ts +118 -0
  61. package/dist/maintenance/index.js +17 -0
  62. package/dist/maintenance/index.js.map +1 -0
  63. package/dist/platform/index.d.ts +16 -1
  64. package/dist/platform/index.js +2 -0
  65. package/dist/platform/index.js.map +1 -1
  66. package/dist/platform/index.native.d.ts +2 -2
  67. package/dist/platform/index.native.js +2 -1
  68. package/dist/platform/index.web.d.ts +1 -1
  69. package/dist/platform/index.web.js +2 -1
  70. package/dist/pol-attachment-queue-C7YNXXhK.d.ts +676 -0
  71. package/dist/provider/index.d.ts +447 -21
  72. package/dist/provider/index.js +33 -13
  73. package/dist/storage/index.d.ts +6 -0
  74. package/dist/storage/index.js +28 -0
  75. package/dist/storage/index.js.map +1 -0
  76. package/dist/storage/index.native.d.ts +6 -0
  77. package/dist/storage/index.native.js +26 -0
  78. package/dist/storage/index.native.js.map +1 -0
  79. package/dist/storage/index.web.d.ts +6 -0
  80. package/dist/storage/index.web.js +26 -0
  81. package/dist/storage/index.web.js.map +1 -0
  82. package/dist/storage/upload/index.d.ts +55 -0
  83. package/dist/storage/upload/index.js +15 -0
  84. package/dist/storage/upload/index.js.map +1 -0
  85. package/dist/storage/upload/index.native.d.ts +57 -0
  86. package/dist/storage/upload/index.native.js +14 -0
  87. package/dist/storage/upload/index.native.js.map +1 -0
  88. package/dist/storage/upload/index.web.d.ts +5 -0
  89. package/dist/storage/upload/index.web.js +14 -0
  90. package/dist/storage/upload/index.web.js.map +1 -0
  91. package/dist/{index-l3iL9Jte.d.ts → supabase-connector-qLm-WHkM.d.ts} +90 -25
  92. package/dist/sync/index.d.ts +288 -23
  93. package/dist/sync/index.js +22 -10
  94. package/dist/types-BVacP54t.d.ts +52 -0
  95. package/dist/types-Bgvx7-E8.d.ts +187 -0
  96. package/dist/{types-afHtE1U_.d.ts → types-CDqWh56B.d.ts} +2 -0
  97. package/package.json +72 -2
  98. package/dist/chunk-32OLICZO.js +0 -1
  99. package/dist/chunk-3AYXHQ4W.js.map +0 -1
  100. package/dist/chunk-5FIMA26D.js +0 -1
  101. package/dist/chunk-62J2DPKX.js.map +0 -1
  102. package/dist/chunk-7EMDVIZX.js.map +0 -1
  103. package/dist/chunk-EJ23MXPQ.js.map +0 -1
  104. package/dist/chunk-FPTDATY5.js.map +0 -1
  105. package/dist/chunk-KCDG2MNP.js +0 -1431
  106. package/dist/chunk-KCDG2MNP.js.map +0 -1
  107. package/dist/chunk-OLHGI472.js +0 -1
  108. package/dist/chunk-PAFBKNL3.js +0 -99
  109. package/dist/chunk-PAFBKNL3.js.map +0 -1
  110. package/dist/chunk-R4YFWQ3Q.js.map +0 -1
  111. package/dist/chunk-V6LJ6MR2.js +0 -740
  112. package/dist/chunk-V6LJ6MR2.js.map +0 -1
  113. package/dist/chunk-VJCL2SWD.js +0 -1
  114. package/dist/failed-upload-store-C0cLxxPz.d.ts +0 -33
  115. /package/dist/{chunk-32OLICZO.js.map → chunk-DGUM43GV.js.map} +0 -0
  116. /package/dist/{chunk-5FIMA26D.js.map → chunk-GKF7TOMT.js.map} +0 -0
  117. /package/dist/{chunk-OLHGI472.js.map → chunk-KGSFAE5B.js.map} +0 -0
  118. /package/dist/{chunk-VJCL2SWD.js.map → chunk-LNL64IJZ.js.map} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pol-studios/powersync",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "Enterprise PowerSync integration for offline-first applications",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -54,6 +54,62 @@
54
54
  "./sync": {
55
55
  "import": "./dist/sync/index.js",
56
56
  "types": "./dist/sync/index.d.ts"
57
+ },
58
+ "./maintenance": {
59
+ "react-native": {
60
+ "import": "./dist/maintenance/index.js",
61
+ "types": "./dist/maintenance/index.d.ts"
62
+ },
63
+ "browser": {
64
+ "import": "./dist/maintenance/index.js",
65
+ "types": "./dist/maintenance/index.d.ts"
66
+ },
67
+ "default": {
68
+ "import": "./dist/maintenance/index.js",
69
+ "types": "./dist/maintenance/index.d.ts"
70
+ }
71
+ },
72
+ "./storage": {
73
+ "react-native": {
74
+ "import": "./dist/storage/index.native.js",
75
+ "types": "./dist/storage/index.native.d.ts"
76
+ },
77
+ "browser": {
78
+ "import": "./dist/storage/index.web.js",
79
+ "types": "./dist/storage/index.web.d.ts"
80
+ },
81
+ "default": {
82
+ "import": "./dist/storage/index.js",
83
+ "types": "./dist/storage/index.d.ts"
84
+ }
85
+ },
86
+ "./storage/upload": {
87
+ "react-native": {
88
+ "import": "./dist/storage/upload/index.native.js",
89
+ "types": "./dist/storage/upload/index.native.d.ts"
90
+ },
91
+ "browser": {
92
+ "import": "./dist/storage/upload/index.web.js",
93
+ "types": "./dist/storage/upload/index.web.d.ts"
94
+ },
95
+ "default": {
96
+ "import": "./dist/storage/upload/index.js",
97
+ "types": "./dist/storage/upload/index.d.ts"
98
+ }
99
+ },
100
+ "./error": {
101
+ "react-native": {
102
+ "import": "./dist/error/index.js",
103
+ "types": "./dist/error/index.d.ts"
104
+ },
105
+ "browser": {
106
+ "import": "./dist/error/index.js",
107
+ "types": "./dist/error/index.d.ts"
108
+ },
109
+ "default": {
110
+ "import": "./dist/error/index.js",
111
+ "types": "./dist/error/index.d.ts"
112
+ }
57
113
  }
58
114
  },
59
115
  "files": [
@@ -76,8 +132,11 @@
76
132
  "@tanstack/react-query": ">=5.0.0",
77
133
  "expo-file-system": ">=15.0.0",
78
134
  "expo-image-manipulator": ">=11.0.0",
135
+ "expo-background-task": ">=0.0.1",
136
+ "expo-task-manager": ">=12.0.0",
79
137
  "react": ">=18.0.0",
80
- "react-native": ">=0.70.0"
138
+ "react-native": ">=0.70.0",
139
+ "react-native-background-upload": ">=6.0.0"
81
140
  },
82
141
  "peerDependenciesMeta": {
83
142
  "@powersync/react-native": {
@@ -104,8 +163,17 @@
104
163
  "expo-image-manipulator": {
105
164
  "optional": true
106
165
  },
166
+ "expo-background-task": {
167
+ "optional": true
168
+ },
169
+ "expo-task-manager": {
170
+ "optional": true
171
+ },
107
172
  "react-native": {
108
173
  "optional": true
174
+ },
175
+ "react-native-background-upload": {
176
+ "optional": true
109
177
  }
110
178
  },
111
179
  "dependencies": {
@@ -117,6 +185,8 @@
117
185
  "@powersync/op-sqlite": "^0.8.0",
118
186
  "@types/react": "^18.0.0",
119
187
  "babel-plugin-react-compiler": "^1.0.0",
188
+ "expo-background-task": "^0.0.1",
189
+ "expo-task-manager": "^12.0.0",
120
190
  "tsup": "^8.0.0",
121
191
  "typescript": "^5.0.0"
122
192
  },
@@ -1 +0,0 @@
1
- //# sourceMappingURL=chunk-32OLICZO.js.map
@@ -1 +0,0 @@
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":[]}
@@ -1 +0,0 @@
1
- //# sourceMappingURL=chunk-5FIMA26D.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/connector/types.ts","../src/conflicts/detect.ts","../src/utils/retry.ts","../src/connector/supabase-connector.ts"],"sourcesContent":["/**\n * Connector Types for @pol-studios/powersync\n *\n * Defines interfaces for PowerSync backend connectors.\n */\n\nimport type { SupabaseClient } from '@supabase/supabase-js';\nimport type { AbstractPowerSyncDatabase, ClassifiedError, CrudEntry } from '../core/types';\nimport type { LoggerAdapter } from '../platform/types';\nimport type { ConflictHandler, ConflictDetectionConfig } from '../conflicts/types';\nimport type { ConflictBus } from '../conflicts/conflict-bus';\n\n// Re-export ConflictBus type for convenience\nexport type { ConflictBus } from '../conflicts/conflict-bus';\n\n// ─── Connector Configuration ─────────────────────────────────────────────────\n\n/**\n * Options for creating a SupabaseConnector\n */\nexport interface SupabaseConnectorOptions {\n /** Supabase client instance */\n supabaseClient: SupabaseClient;\n /** PowerSync service URL */\n powerSyncUrl: string;\n /**\n * Optional: Custom schema routing function.\n * Determines which Supabase schema a table belongs to.\n * @default Returns 'public' for all tables\n */\n schemaRouter?: SchemaRouter;\n /**\n * Optional: Custom CRUD handler for complex mutations.\n * Allows overriding default upsert/update/delete behavior.\n */\n crudHandler?: CrudHandler;\n /** Logger for debugging */\n logger?: LoggerAdapter;\n /** Called when a transaction is successfully uploaded */\n onTransactionSuccess?: (entries: CrudEntry[]) => void;\n /** Called when a transaction fails to upload */\n onTransactionFailure?: (entries: CrudEntry[], error: Error, classified: ClassifiedError) => void;\n /** Called when a transaction is fully completed (after transaction.complete()) */\n onTransactionComplete?: (entries: CrudEntry[]) => void;\n /** Function to check if upload should proceed. Used for sync mode gating. */\n shouldUpload?: () => boolean;\n /**\n * Optional: Configuration for version-based conflict detection.\n * When enabled, checks for conflicts before uploading changes.\n */\n conflictDetection?: ConflictDetectionConfig;\n /**\n * Optional: Handler for conflict resolution.\n * If not provided, conflicts are logged and upload proceeds.\n */\n conflictHandler?: ConflictHandler;\n /**\n * Optional: Event bus for publishing conflict events.\n * Use this to notify the UI layer about detected conflicts.\n */\n conflictBus?: ConflictBus;\n /**\n * Optional: Configuration for retry behavior on upload failures.\n * Allows customizing retry attempts, delays, and backoff for different error types.\n * @default DEFAULT_RETRY_CONFIG\n */\n retryConfig?: Partial<RetryConfig>;\n}\n\n/**\n * Configuration passed to PowerSyncProvider for connector setup\n */\nexport interface ConnectorConfig {\n /**\n * Custom schema routing function.\n * Determines which Supabase schema a table belongs to.\n * @default Returns 'public' for all tables\n *\n * @example\n * ```typescript\n * schemaRouter: (table) => {\n * if (['Profile', 'Comment'].includes(table)) return 'core';\n * return 'public';\n * }\n * ```\n */\n schemaRouter?: SchemaRouter;\n\n /**\n * Custom CRUD handler for complex mutations.\n * @default Uses standard upsert/update/delete operations\n */\n crudHandler?: CrudHandler;\n\n /**\n * Token refresh configuration.\n */\n tokenRefresh?: {\n /** Refresh token when it expires within this many seconds (default: 60) */\n refreshThresholdSeconds?: number;\n };\n\n /**\n * Optional retry configuration for upload failures.\n * Allows customizing retry attempts, delays, and backoff for different error types.\n * @default DEFAULT_RETRY_CONFIG\n */\n retryConfig?: Partial<RetryConfig>;\n}\n\n// ─── Schema Routing ──────────────────────────────────────────────────────────\n\n/**\n * Function that determines which Supabase schema a table belongs to.\n *\n * @param tableName - The name of the table\n * @returns The schema name (e.g., 'public', 'core')\n */\nexport type SchemaRouter = (tableName: string) => string;\n\n/**\n * Default schema router that returns 'public' for all tables\n */\nexport const defaultSchemaRouter: SchemaRouter = () => 'public';\n\n// ─── CRUD Handling ───────────────────────────────────────────────────────────\n\n/**\n * Custom handler for CRUD operations.\n *\n * Return `true` from a handler to indicate the operation was handled.\n * Return `false` to fall back to default behavior.\n */\nexport interface CrudHandler {\n /**\n * Handle a PUT operation (insert or replace).\n * @param entry - The CRUD entry\n * @param supabase - Supabase client\n * @param schema - The resolved schema for this table\n * @returns true if handled, false to use default behavior\n */\n handlePut?(entry: CrudEntry, supabase: SupabaseClient, schema: string): Promise<boolean>;\n\n /**\n * Handle a PATCH operation (update).\n * @param entry - The CRUD entry\n * @param supabase - Supabase client\n * @param schema - The resolved schema for this table\n * @returns true if handled, false to use default behavior\n */\n handlePatch?(entry: CrudEntry, supabase: SupabaseClient, schema: string): Promise<boolean>;\n\n /**\n * Handle a DELETE operation.\n * @param entry - The CRUD entry\n * @param supabase - Supabase client\n * @param schema - The resolved schema for this table\n * @returns true if handled, false to use default behavior\n */\n handleDelete?(entry: CrudEntry, supabase: SupabaseClient, schema: string): Promise<boolean>;\n}\n\n// ─── Credentials ─────────────────────────────────────────────────────────────\n\n/**\n * Credentials returned by fetchCredentials\n */\nexport interface PowerSyncCredentials {\n /** PowerSync service endpoint URL */\n endpoint: string;\n /** JWT token for authentication */\n token: string;\n /** When the token expires */\n expiresAt?: Date;\n}\n\n// ─── Retry Configuration ─────────────────────────────────────────────────────\n\n/**\n * Configuration for a single retry category\n */\nexport interface RetryStrategyConfig {\n /** Maximum number of retry attempts */\n maxRetries: number;\n /** Initial delay in milliseconds */\n baseDelayMs: number;\n /** Maximum delay cap in milliseconds */\n maxDelayMs: number;\n /** Multiplier for exponential backoff */\n backoffMultiplier: number;\n}\n\n/**\n * Full retry configuration for the connector\n */\nexport interface RetryConfig {\n /** Retry config for transient errors (network, server 5xx) */\n transient: RetryStrategyConfig;\n /** Retry config for permanent errors (RLS, validation, constraints) */\n permanent: RetryStrategyConfig;\n}\n\n/**\n * Default retry configuration\n */\nexport const DEFAULT_RETRY_CONFIG: RetryConfig = {\n transient: {\n maxRetries: 3,\n baseDelayMs: 1000,\n maxDelayMs: 30000,\n backoffMultiplier: 2\n },\n permanent: {\n maxRetries: 2,\n baseDelayMs: 5000,\n maxDelayMs: 300000,\n backoffMultiplier: 3\n }\n};\n\n// ─── Re-export from Core ─────────────────────────────────────────────────────\n\n// Re-export PowerSyncBackendConnector from core/types to maintain API compatibility\nexport type { PowerSyncBackendConnector } from '../core/types';","/**\n * Conflict Detection for @pol-studios/powersync\n *\n * Provides version-based conflict detection using AuditLog attribution.\n * Only queries AuditLog when version mismatch is detected.\n */\n\nimport type { SupabaseClient } from '@supabase/supabase-js';\nimport type { AbstractPowerSyncDatabase } from '../core/types';\nimport type { FieldConflict, ConflictCheckResult, ConflictDetectionConfig } from './types';\n\n/**\n * Regex to validate table names and prevent SQL injection.\n * Only allows alphanumeric characters and underscores, starting with a letter or underscore.\n */\nconst TABLE_NAME_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]*$/;\n\n/**\n * Validates a table name to prevent SQL injection.\n * @throws Error if the table name is invalid\n */\nfunction validateTableName(table: string): void {\n if (!TABLE_NAME_REGEX.test(table)) {\n throw new Error(`Invalid table name: ${table}`);\n }\n}\nconst DEFAULT_IGNORED_FIELDS = ['updatedAt', 'createdAt', '_version', 'id'];\n\n/**\n * Detect conflicts between local pending changes and server state.\n *\n * Uses a two-step approach:\n * 1. Version match (local._version == server._version) → Sync immediately\n * 2. Version mismatch → Query AuditLog for field changes and attribution\n *\n * @param table - The table name\n * @param recordId - The record ID\n * @param localVersion - Version number from local SQLite\n * @param serverVersion - Version number from server (fetched separately)\n * @param pendingChanges - Fields with local changes to sync\n * @param supabase - Supabase client for AuditLog queries\n * @param config - Optional detection configuration\n * @returns Conflict check result with field-level details\n */\nexport async function detectConflicts(table: string, recordId: string, localVersion: number, serverVersion: number, pendingChanges: Record<string, unknown>, supabase: SupabaseClient, config?: ConflictDetectionConfig): Promise<ConflictCheckResult> {\n const ignoredFields = new Set([...DEFAULT_IGNORED_FIELDS, ...(config?.ignoredFields ?? [])]);\n\n // Filter out ignored fields from pending changes\n const filteredPendingChanges: Record<string, unknown> = {};\n for (const [field, value] of Object.entries(pendingChanges)) {\n if (!ignoredFields.has(field)) {\n filteredPendingChanges[field] = value;\n }\n }\n\n // Step 1: Version match = no conflict possible\n if (localVersion === serverVersion) {\n return {\n hasConflict: false,\n conflicts: [],\n nonConflictingChanges: Object.keys(filteredPendingChanges),\n table,\n recordId\n };\n }\n\n // Step 2: Version mismatch - query AuditLog for changes since our version\n const {\n data: auditLogs,\n error\n } = await supabase.schema('core').from('AuditLog').select('oldRecord, newRecord, changeBy, changeAt').eq('tableName', table).eq('recordId_text', recordId).order('changeAt', {\n ascending: false\n }).limit(20); // Recent changes should be sufficient\n\n if (error) {\n console.warn('[detectConflicts] Failed to query AuditLog:', error);\n // On error, assume no conflict and let sync proceed\n // (Server will reject if there's a real issue)\n return {\n hasConflict: false,\n conflicts: [],\n nonConflictingChanges: Object.keys(filteredPendingChanges),\n table,\n recordId\n };\n }\n\n // Build map of server-changed fields with attribution\n // Key: field name, Value: most recent change info\n const serverChanges = new Map<string, {\n newValue: unknown;\n changedBy: string | null;\n changedAt: Date;\n }>();\n for (const log of auditLogs ?? []) {\n const oldRec = log.oldRecord as Record<string, unknown> | null;\n const newRec = log.newRecord as Record<string, unknown> | null;\n if (!oldRec || !newRec) continue;\n for (const [field, newValue] of Object.entries(newRec)) {\n // Skip ignored fields\n if (ignoredFields.has(field)) continue;\n\n // Only track if field actually changed AND we don't already have a more recent change\n if (oldRec[field] !== newValue && !serverChanges.has(field)) {\n serverChanges.set(field, {\n newValue,\n changedBy: log.changeBy as string | null,\n changedAt: new Date(log.changeAt as string)\n });\n }\n }\n }\n\n // Compare pending changes against server changes\n const conflicts: FieldConflict[] = [];\n const nonConflictingChanges: string[] = [];\n for (const [field, localValue] of Object.entries(filteredPendingChanges)) {\n if (serverChanges.has(field)) {\n // This field was changed on server - conflict!\n const serverChange = serverChanges.get(field)!;\n conflicts.push({\n field,\n localValue,\n serverValue: serverChange.newValue,\n changedBy: serverChange.changedBy,\n changedAt: serverChange.changedAt\n });\n } else {\n // Field wasn't changed on server - safe to sync\n nonConflictingChanges.push(field);\n }\n }\n return {\n hasConflict: conflicts.length > 0,\n conflicts,\n nonConflictingChanges,\n table,\n recordId\n };\n}\n\n/**\n * Check if a table has a _version column for conflict detection.\n *\n * @param table - The table name\n * @param db - PowerSync database instance\n * @returns True if the table has version tracking\n */\nexport async function hasVersionColumn(table: string, db: AbstractPowerSyncDatabase): Promise<boolean> {\n try {\n // Validate table name to prevent SQL injection\n validateTableName(table);\n\n // Query the PowerSync internal schema for column info\n const result = await db.getAll<{\n name: string;\n }>(`PRAGMA table_info(\"${table}\")`);\n return result.some(col => col.name === '_version');\n } catch {\n return false;\n }\n}\n\n/**\n * Fetch the current server version for a record.\n *\n * @param table - The table name\n * @param recordId - The record ID\n * @param schema - The Supabase schema (default: 'public')\n * @param supabase - Supabase client\n * @returns The server version number, or null if record not found\n */\nexport async function fetchServerVersion(table: string, recordId: string, schema: string, supabase: SupabaseClient): Promise<number | null> {\n const query = schema === 'public' ? supabase.from(table) : (supabase.schema(schema) as unknown as ReturnType<typeof supabase.schema>).from(table);\n const {\n data,\n error\n } = await query.select('_version').eq('id', recordId).single();\n if (error || !data) {\n return null;\n }\n return (data as {\n _version?: number;\n })._version ?? null;\n}\n\n/**\n * Get the local version for a record from PowerSync SQLite.\n *\n * @param table - The table name\n * @param recordId - The record ID\n * @param db - PowerSync database instance\n * @returns The local version number, or null if not found\n */\nexport async function getLocalVersion(table: string, recordId: string, db: AbstractPowerSyncDatabase): Promise<number | null> {\n // Validate table name to prevent SQL injection\n validateTableName(table);\n const result = await db.get<{\n _version?: number;\n }>(`SELECT _version FROM \"${table}\" WHERE id = ?`, [recordId]);\n return result?._version ?? null;\n}","/**\n * Exponential backoff retry utilities for resilient network operations.\n *\n * Provides configurable retry logic with exponential backoff, jitter,\n * and abort signal support for cancellation.\n */\n\n/**\n * Configuration for exponential backoff retry behavior.\n */\nexport interface BackoffConfig {\n /** Maximum number of retry attempts (0 = no retries, just one attempt) */\n maxRetries: number;\n /** Base delay in milliseconds before first retry */\n baseDelayMs: number;\n /** Maximum delay cap in milliseconds */\n maxDelayMs: number;\n /** Multiplier applied to delay for each subsequent retry (typically 2) */\n backoffMultiplier: number;\n}\n\n/**\n * Options for the withExponentialBackoff function.\n */\nexport interface BackoffOptions {\n /** Optional AbortSignal for cancellation support */\n signal?: AbortSignal;\n /** Callback invoked before each retry attempt */\n onRetry?: (attempt: number, delay: number, error: Error) => void;\n}\n\n/**\n * Error thrown when an operation is aborted via AbortSignal.\n */\nexport class AbortError extends Error {\n constructor(message = 'Operation aborted') {\n super(message);\n this.name = 'AbortError';\n }\n}\n\n/**\n * Error thrown when all retry attempts have been exhausted.\n */\nexport class RetryExhaustedError extends Error {\n /** The last error that caused the final retry to fail */\n readonly cause: Error;\n /** Total number of attempts made */\n readonly attempts: number;\n constructor(cause: Error, attempts: number) {\n super(`Retry exhausted after ${attempts} attempt(s): ${cause.message}`);\n this.name = 'RetryExhaustedError';\n this.cause = cause;\n this.attempts = attempts;\n }\n}\n\n/**\n * Calculates the delay for a given retry attempt using exponential backoff.\n *\n * Formula: min(baseDelay * (multiplier ^ attempt), maxDelay)\n *\n * @param attempt - The current attempt number (0-indexed)\n * @param config - Backoff configuration\n * @returns Delay in milliseconds (without jitter)\n *\n * @example\n * ```ts\n * const config = { baseDelayMs: 1000, maxDelayMs: 30000, backoffMultiplier: 2 };\n * calculateBackoffDelay(0, config); // 1000\n * calculateBackoffDelay(1, config); // 2000\n * calculateBackoffDelay(2, config); // 4000\n * calculateBackoffDelay(5, config); // 30000 (capped at maxDelayMs)\n * ```\n */\nexport function calculateBackoffDelay(attempt: number, config: Pick<BackoffConfig, 'baseDelayMs' | 'maxDelayMs' | 'backoffMultiplier'>): number {\n const {\n baseDelayMs,\n maxDelayMs,\n backoffMultiplier\n } = config;\n\n // Ensure non-negative attempt number\n const safeAttempt = Math.max(0, attempt);\n\n // Calculate exponential delay\n const exponentialDelay = baseDelayMs * Math.pow(backoffMultiplier, safeAttempt);\n\n // Cap at maxDelayMs\n return Math.min(exponentialDelay, maxDelayMs);\n}\n\n/**\n * Adds jitter (±10%) to a delay value to prevent thundering herd.\n *\n * @param delay - Base delay in milliseconds\n * @returns Delay with random jitter applied\n */\nexport function addJitter(delay: number): number {\n // Generate random factor between 0.9 and 1.1 (±10%)\n const jitterFactor = 0.9 + Math.random() * 0.2;\n return Math.round(delay * jitterFactor);\n}\n\n/**\n * Sleep utility that respects AbortSignal for cancellation.\n *\n * @param ms - Duration to sleep in milliseconds\n * @param signal - Optional AbortSignal for cancellation\n * @returns Promise that resolves after the delay or rejects if aborted\n * @throws {AbortError} If the signal is aborted during sleep\n *\n * @example\n * ```ts\n * const controller = new AbortController();\n *\n * // Sleep for 1 second\n * await sleep(1000);\n *\n * // Sleep with cancellation support\n * await sleep(1000, controller.signal);\n *\n * // Cancel the sleep\n * controller.abort();\n * ```\n */\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n // Check if already aborted\n if (signal?.aborted) {\n reject(new AbortError());\n return;\n }\n\n // Handle zero or negative duration\n if (ms <= 0) {\n resolve();\n return;\n }\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n const handleAbort = () => {\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n reject(new AbortError());\n };\n\n // Set up abort listener if signal provided\n if (signal) {\n signal.addEventListener('abort', handleAbort, {\n once: true\n });\n }\n timeoutId = setTimeout(() => {\n // Clean up abort listener\n if (signal) {\n signal.removeEventListener('abort', handleAbort);\n }\n resolve();\n }, ms);\n });\n}\n\n/**\n * Default backoff configuration suitable for most network operations.\n */\nexport const DEFAULT_BACKOFF_CONFIG: BackoffConfig = {\n maxRetries: 3,\n baseDelayMs: 1000,\n maxDelayMs: 30000,\n backoffMultiplier: 2\n};\n\n/**\n * Executes a function with exponential backoff retry logic.\n *\n * Retries the provided function on failure using exponential backoff with jitter.\n * Supports cancellation via AbortSignal and provides hooks for retry observation.\n *\n * @param fn - Async function to execute with retry logic\n * @param config - Backoff configuration (maxRetries, delays, multiplier)\n * @param options - Optional abort signal and retry callback\n * @returns Promise resolving to the function result\n * @throws {AbortError} If operation is aborted via signal\n * @throws {RetryExhaustedError} If all retry attempts fail\n *\n * @example\n * ```ts\n * const controller = new AbortController();\n *\n * const result = await withExponentialBackoff(\n * async () => {\n * const response = await fetch('https://api.example.com/data');\n * if (!response.ok) throw new Error('Request failed');\n * return response.json();\n * },\n * {\n * maxRetries: 3,\n * baseDelayMs: 1000,\n * maxDelayMs: 30000,\n * backoffMultiplier: 2,\n * },\n * {\n * signal: controller.signal,\n * onRetry: (attempt, delay, error) => {\n * console.log(`Retry ${attempt} in ${delay}ms: ${error.message}`);\n * },\n * }\n * );\n * ```\n */\nexport async function withExponentialBackoff<T>(fn: () => Promise<T>, config: BackoffConfig, options?: BackoffOptions): Promise<T> {\n const {\n maxRetries,\n baseDelayMs,\n maxDelayMs,\n backoffMultiplier\n } = config;\n const {\n signal,\n onRetry\n } = options ?? {};\n\n // Check for immediate abort\n if (signal?.aborted) {\n throw new AbortError();\n }\n\n // Validate config\n const safeMaxRetries = Math.max(0, Math.floor(maxRetries));\n const totalAttempts = safeMaxRetries + 1; // Initial attempt + retries\n\n let lastError: Error | undefined;\n for (let attempt = 0; attempt < totalAttempts; attempt++) {\n // Check for abort before each attempt\n if (signal?.aborted) {\n throw new AbortError();\n }\n try {\n return await fn();\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n\n // Check if this was the last attempt\n const isLastAttempt = attempt === totalAttempts - 1;\n if (isLastAttempt) {\n break;\n }\n\n // Check for abort before sleeping\n if (signal?.aborted) {\n throw new AbortError();\n }\n\n // Calculate delay with jitter\n const baseDelay = calculateBackoffDelay(attempt, {\n baseDelayMs,\n maxDelayMs,\n backoffMultiplier\n });\n const delayWithJitter = addJitter(baseDelay);\n\n // Notify about upcoming retry\n onRetry?.(attempt + 1, delayWithJitter, lastError);\n\n // Wait before retry\n await sleep(delayWithJitter, signal);\n }\n }\n\n // All attempts exhausted\n throw new RetryExhaustedError(lastError!, totalAttempts);\n}","/**\n * Supabase Connector for PowerSync\n *\n * A generic, configurable connector that handles:\n * - Authentication with Supabase JWT tokens\n * - Uploading local changes back to Supabase\n * - Schema routing for multi-schema databases\n * - Custom CRUD handling for complex operations\n * - Version-based conflict detection (when enabled)\n */\n\nimport type { SupabaseClient } from '@supabase/supabase-js';\nimport type { AbstractPowerSyncDatabase, ClassifiedError, CrudEntry } from '../core/types';\nimport type { LoggerAdapter } from '../platform/types';\nimport type { SupabaseConnectorOptions, PowerSyncBackendConnector, PowerSyncCredentials, SchemaRouter, CrudHandler, RetryConfig } from './types';\nimport type { ConflictBus } from '../conflicts/conflict-bus';\nimport type { ConflictHandler, ConflictDetectionConfig, ConflictCheckResult, ConflictResolution } from '../conflicts/types';\nimport { defaultSchemaRouter, DEFAULT_RETRY_CONFIG } from './types';\nimport { classifySupabaseError } from '../core/errors';\nimport { detectConflicts, fetchServerVersion, getLocalVersion, hasVersionColumn } from '../conflicts/detect';\nimport { withExponentialBackoff, calculateBackoffDelay } from '../utils/retry';\nimport { failedUploadStore, type FailedUpload } from '../sync/failed-upload-store';\n\n/**\n * Update type enum matching @powersync/common\n */\nenum UpdateType {\n PUT = 'PUT',\n PATCH = 'PATCH',\n DELETE = 'DELETE',\n}\n\n/**\n * Generic Supabase connector for PowerSync.\n *\n * This connector handles authentication and CRUD uploads to Supabase.\n * It supports configurable schema routing and custom CRUD handlers\n * for complex use cases.\n *\n * @example Basic usage\n * ```typescript\n * const connector = new SupabaseConnector({\n * supabaseClient: supabase,\n * powerSyncUrl: 'https://your-powersync-instance.com',\n * });\n * ```\n *\n * @example With schema routing\n * ```typescript\n * const connector = new SupabaseConnector({\n * supabaseClient: supabase,\n * powerSyncUrl: 'https://your-powersync-instance.com',\n * schemaRouter: (table) => {\n * if (['Profile', 'Comment', 'CommentSection'].includes(table)) {\n * return 'core';\n * }\n * return 'public';\n * },\n * });\n * ```\n *\n * @example With custom CRUD handler\n * ```typescript\n * const connector = new SupabaseConnector({\n * supabaseClient: supabase,\n * powerSyncUrl: 'https://your-powersync-instance.com',\n * crudHandler: {\n * async handlePut(entry, supabase, schema) {\n * // Custom handling for specific tables\n * if (entry.table === 'SpecialTable') {\n * await myCustomUpsert(entry);\n * return true; // Handled\n * }\n * return false; // Use default\n * },\n * },\n * });\n * ```\n */\nexport class SupabaseConnector implements PowerSyncBackendConnector {\n private readonly supabase: SupabaseClient;\n private readonly powerSyncUrl: string;\n private readonly schemaRouter: SchemaRouter;\n private readonly crudHandler?: CrudHandler;\n private readonly logger?: LoggerAdapter;\n private readonly onTransactionSuccess?: (entries: CrudEntry[]) => void;\n private readonly onTransactionFailure?: (entries: CrudEntry[], error: Error, classified: ClassifiedError) => void;\n private readonly onTransactionComplete?: (entries: CrudEntry[]) => void;\n private readonly shouldUploadFn?: () => boolean;\n\n // Conflict detection configuration\n private readonly conflictDetection?: ConflictDetectionConfig;\n private readonly conflictHandler?: ConflictHandler;\n private readonly conflictBus?: ConflictBus;\n\n // Cache for version column existence checks (table -> hasVersionColumn)\n private versionColumnCache = new Map<string, boolean>();\n\n // Active project IDs for scoped sync (optional feature)\n private activeProjectIds: string[] = [];\n\n // Store resolutions for retry - when PowerSync retries, we apply stored resolutions\n // instead of re-detecting conflicts that the user already resolved\n private resolvedConflicts = new Map<string, ConflictResolution>();\n\n // Cleanup function for resolution listener subscription\n private unsubscribeResolution?: () => void;\n\n // Retry configuration\n private retryConfig: RetryConfig;\n private autoRetryPaused: boolean = false;\n constructor(options: SupabaseConnectorOptions) {\n this.supabase = options.supabaseClient;\n this.powerSyncUrl = options.powerSyncUrl;\n this.schemaRouter = options.schemaRouter ?? defaultSchemaRouter;\n this.crudHandler = options.crudHandler;\n this.logger = options.logger;\n this.onTransactionSuccess = options.onTransactionSuccess;\n this.onTransactionFailure = options.onTransactionFailure;\n this.onTransactionComplete = options.onTransactionComplete;\n this.shouldUploadFn = options.shouldUpload;\n\n // Conflict detection options\n this.conflictDetection = options.conflictDetection;\n this.conflictHandler = options.conflictHandler;\n this.conflictBus = options.conflictBus;\n\n // Initialize retry configuration by merging user options with defaults\n this.retryConfig = {\n transient: {\n ...DEFAULT_RETRY_CONFIG.transient,\n ...options.retryConfig?.transient\n },\n permanent: {\n ...DEFAULT_RETRY_CONFIG.permanent,\n ...options.retryConfig?.permanent\n }\n };\n\n // Subscribe to resolution events from the conflict bus\n // This stores resolutions so that on retry, we apply them instead of re-detecting\n if (this.conflictBus) {\n this.unsubscribeResolution = this.conflictBus.onResolution((table, recordId, resolution) => {\n // Key by table:recordId to avoid collisions across tables with same UUID\n const key = `${table}:${recordId}`;\n if (__DEV__) {\n console.log('[Connector] Storing resolution for retry:', {\n table,\n recordId,\n key,\n resolution\n });\n }\n this.resolvedConflicts.set(key, resolution);\n });\n }\n }\n\n /**\n * Clean up resources (unsubscribe from event listeners).\n * Call this when the connector is no longer needed.\n */\n destroy(): void {\n if (this.unsubscribeResolution) {\n this.unsubscribeResolution();\n this.unsubscribeResolution = undefined;\n }\n this.resolvedConflicts.clear();\n }\n\n // ─── Retry Control Methods ─────────────────────────────────────────────────\n\n /**\n * Pause automatic retry of failed uploads.\n * Use this when the user goes offline intentionally or wants manual control.\n */\n pauseAutoRetry(): void {\n this.autoRetryPaused = true;\n if (__DEV__) {\n console.log('[Connector] Auto-retry paused');\n }\n }\n\n /**\n * Resume automatic retry of failed uploads.\n */\n resumeAutoRetry(): void {\n this.autoRetryPaused = false;\n if (__DEV__) {\n console.log('[Connector] Auto-retry resumed');\n }\n }\n\n /**\n * Manually retry all failed uploads that are ready for retry.\n * This processes entries from the failed upload store.\n */\n async retryFailedUploads(): Promise<void> {\n const retryableUploads = failedUploadStore.getRetryable();\n if (retryableUploads.length === 0) {\n if (__DEV__) {\n console.log('[Connector] No failed uploads ready for retry');\n }\n return;\n }\n if (__DEV__) {\n console.log('[Connector] Manually retrying failed uploads:', {\n count: retryableUploads.length,\n entries: retryableUploads.map(u => ({\n table: u.table,\n id: u.id,\n operation: u.operation\n }))\n });\n }\n for (const upload of retryableUploads) {\n try {\n // Reconstruct the CrudEntry from the failed upload\n // Use a synthetic clientId since we don't have the original\n const entry: CrudEntry = {\n table: upload.table,\n op: upload.operation,\n id: upload.id,\n clientId: Date.now(),\n // Synthetic clientId for retry\n opData: upload.data\n };\n await this.processWithRetry(entry);\n\n // Success - remove from failed store\n failedUploadStore.remove(upload.id);\n if (__DEV__) {\n console.log('[Connector] Retry succeeded for:', {\n table: upload.table,\n id: upload.id\n });\n }\n } catch (error) {\n // Failed again - update is handled in processWithRetry\n if (__DEV__) {\n console.log('[Connector] Retry failed again for:', {\n table: upload.table,\n id: upload.id,\n error: error instanceof Error ? error.message : String(error)\n });\n }\n }\n }\n }\n\n /**\n * Clear all failed uploads from the store.\n * Use with caution - this discards all pending retries.\n */\n clearFailedUploads(): void {\n failedUploadStore.clear();\n if (__DEV__) {\n console.log('[Connector] Failed uploads cleared');\n }\n }\n\n /**\n * Get all failed uploads from the store.\n */\n getFailedUploads(): FailedUpload[] {\n return failedUploadStore.getAll();\n }\n\n // ─── Private Retry Logic ───────────────────────────────────────────────────\n\n /**\n * Process a single CRUD entry with exponential backoff retry.\n *\n * @param entry - The CRUD entry to process\n * @throws Error if all retries exhausted (for critical failures)\n */\n private async processWithRetry(entry: CrudEntry): Promise<void> {\n const classified = {\n isPermanent: false,\n pgCode: undefined as string | undefined,\n userMessage: ''\n };\n\n // Select retry config based on whether this is a retry from failed store\n // For initial attempts, we'll classify the error after the first failure\n let selectedConfig = this.retryConfig.transient;\n let lastError: Error | undefined;\n try {\n await withExponentialBackoff(async () => {\n try {\n await this.processCrudEntry(entry);\n } catch (error) {\n // Classify the error to determine retry strategy\n const classifiedError = classifySupabaseError(error);\n classified.isPermanent = classifiedError.isPermanent;\n classified.pgCode = classifiedError.pgCode;\n classified.userMessage = classifiedError.userMessage;\n\n // Switch to permanent retry config if error is permanent\n if (classifiedError.isPermanent) {\n selectedConfig = this.retryConfig.permanent;\n }\n lastError = error instanceof Error ? error : new Error(String(error));\n if (__DEV__) {\n console.log('[Connector] CRUD operation failed, will retry:', {\n table: entry.table,\n op: entry.op,\n id: entry.id,\n isPermanent: classifiedError.isPermanent,\n pgCode: classifiedError.pgCode,\n userMessage: classifiedError.userMessage\n });\n }\n throw error;\n }\n }, selectedConfig, {\n onRetry: (attempt, delay, error) => {\n this.logger?.debug('[Connector] Retry attempt:', {\n table: entry.table,\n op: entry.op,\n id: entry.id,\n attempt,\n delay,\n error: error.message\n });\n if (__DEV__) {\n console.log('[Connector] Retry attempt:', {\n table: entry.table,\n op: entry.op,\n id: entry.id,\n attempt,\n delayMs: delay,\n errorMessage: error.message\n });\n }\n }\n });\n } catch (error) {\n // All retries exhausted - add to failed upload store\n const finalError = lastError ?? (error instanceof Error ? error : new Error(String(error)));\n\n // Calculate next retry time based on error category\n const category: 'transient' | 'permanent' | 'unknown' = classified.isPermanent ? 'permanent' : classified.pgCode ? 'transient' : 'unknown';\n const retryConfig = classified.isPermanent ? this.retryConfig.permanent : this.retryConfig.transient;\n\n // Calculate next retry delay (for future manual or scheduled retry)\n const nextRetryDelay = calculateBackoffDelay(retryConfig.maxRetries, retryConfig);\n const nextRetryAt = Date.now() + nextRetryDelay;\n failedUploadStore.add({\n table: entry.table,\n operation: entry.op as 'PUT' | 'PATCH' | 'DELETE',\n data: entry.opData ?? {},\n error: {\n message: finalError.message,\n code: classified.pgCode,\n category\n },\n retryCount: retryConfig.maxRetries,\n lastAttempt: Date.now(),\n nextRetryAt\n });\n if (__DEV__) {\n console.log('[Connector] Entry added to failed upload store:', {\n table: entry.table,\n op: entry.op,\n id: entry.id,\n category,\n nextRetryAt: new Date(nextRetryAt).toISOString()\n });\n }\n this.logger?.error('[Connector] CRUD entry failed after retries:', {\n table: entry.table,\n op: entry.op,\n id: entry.id,\n error: finalError.message,\n category,\n nextRetryAt\n });\n\n // Only re-throw for critical failures that should stop the transaction\n // Non-critical failures are stored for later retry\n if (classified.isPermanent) {\n throw finalError;\n }\n }\n }\n\n /**\n * Set the active project IDs for scoped sync.\n * Call this when user selects/opens projects.\n */\n setActiveProjectIds(projectIds: string[]): void {\n this.activeProjectIds = projectIds;\n }\n\n /**\n * Get the current active project IDs.\n */\n getActiveProjectIds(): string[] {\n return this.activeProjectIds;\n }\n\n /**\n * Get credentials for PowerSync connection.\n * Uses Supabase session token.\n *\n * Note: Token refresh is handled by Supabase's startAutoRefresh() which must be\n * called on app initialization. getSession() returns the auto-refreshed token.\n */\n async fetchCredentials(): Promise<PowerSyncCredentials> {\n this.logger?.debug('[Connector] Fetching credentials...');\n const {\n data: {\n session\n },\n error\n } = await this.supabase.auth.getSession();\n if (error) {\n this.logger?.error('[Connector] Auth error:', error);\n throw new Error(`Failed to get Supabase session: ${error.message}`);\n }\n if (!session) {\n this.logger?.error('[Connector] No active session');\n throw new Error('No active Supabase session');\n }\n this.logger?.debug('[Connector] Credentials fetched, token expires at:', session.expires_at);\n return {\n endpoint: this.powerSyncUrl,\n token: session.access_token,\n expiresAt: session.expires_at ? new Date(session.expires_at * 1000) : undefined\n };\n }\n\n /**\n * Upload local changes to Supabase.\n * Called automatically by PowerSync when there are pending uploads.\n *\n * When conflict detection is enabled:\n * 1. Checks if table has _version column (cached)\n * 2. If yes, compares local vs server version\n * 3. On version mismatch, queries AuditLog for field conflicts\n * 4. If conflicts found, calls handler or publishes to conflict bus\n * 5. Applies resolution or skips entry based on handler response\n */\n async uploadData(database: AbstractPowerSyncDatabase): Promise<void> {\n // Check if uploads are allowed based on sync mode\n if (this.shouldUploadFn && !this.shouldUploadFn()) {\n if (__DEV__) {\n console.log('[Connector] Upload skipped - sync mode does not allow uploads');\n }\n this.logger?.debug('[Connector] Upload skipped - sync mode does not allow uploads');\n return;\n }\n\n // Process any retryable failed uploads if auto-retry is not paused\n if (!this.autoRetryPaused) {\n const retryableUploads = failedUploadStore.getRetryable();\n if (retryableUploads.length > 0) {\n if (__DEV__) {\n console.log('[Connector] Processing retryable failed uploads:', {\n count: retryableUploads.length\n });\n }\n for (const upload of retryableUploads) {\n try {\n // Reconstruct the CrudEntry from the failed upload\n // Use a synthetic clientId since we don't have the original\n const entry: CrudEntry = {\n table: upload.table,\n op: upload.operation,\n id: upload.id,\n clientId: Date.now(),\n // Synthetic clientId for retry\n opData: upload.data\n };\n await this.processWithRetry(entry);\n\n // Success - remove from failed store\n failedUploadStore.remove(upload.id);\n if (__DEV__) {\n console.log('[Connector] Retried upload succeeded:', {\n table: upload.table,\n id: upload.id\n });\n }\n } catch (error) {\n // Failed again - already handled in processWithRetry (updated in store)\n if (__DEV__) {\n console.log('[Connector] Retried upload failed again:', {\n table: upload.table,\n id: upload.id,\n error: error instanceof Error ? error.message : String(error)\n });\n }\n }\n }\n }\n }\n if (__DEV__) {\n console.log('[Connector] uploadData called, fetching next CRUD transaction...');\n }\n const transaction = await database.getNextCrudTransaction();\n if (!transaction) {\n if (__DEV__) {\n console.log('[Connector] No pending CRUD transaction found');\n }\n return;\n }\n if (__DEV__) {\n console.log('[Connector] Transaction fetched:', {\n crudCount: transaction.crud.length,\n entries: transaction.crud.map(e => ({\n table: e.table,\n op: e.op,\n id: e.id,\n opDataKeys: e.opData ? Object.keys(e.opData) : []\n }))\n });\n }\n\n // If conflict detection is disabled, use standard upload\n const conflictDetectionEnabled = this.conflictDetection?.enabled !== false;\n if (!conflictDetectionEnabled) {\n await this.processTransaction(transaction, database);\n return;\n }\n\n // Process with conflict detection\n const {\n crud\n } = transaction;\n const skipTables = new Set(this.conflictDetection?.skipTables ?? []);\n const entriesToProcess: CrudEntry[] = [];\n // Entries queued for UI resolution - these should NOT complete the transaction (leave in queue)\n const entriesQueuedForUI: CrudEntry[] = [];\n // Entries resolved with keep-server - these WILL complete the transaction (discard local changes)\n const entriesDiscarded: CrudEntry[] = [];\n\n // Track partial resolutions to re-emit remaining conflicts after sync completes\n const partialResolutions: Array<{\n originalConflict: ConflictCheckResult;\n syncedFields: string[];\n }> = [];\n for (const entry of crud) {\n // Skip DELETE operations - no conflict checking needed\n if (entry.op === 'DELETE') {\n entriesToProcess.push(entry);\n continue;\n }\n\n // Skip tables in the skip list\n if (skipTables.has(entry.table)) {\n entriesToProcess.push(entry);\n continue;\n }\n\n // Check for existing resolution from a previous retry\n // This prevents re-detecting conflicts that the user already resolved\n // Key by table:recordId to handle same UUID in different tables\n const resolutionKey = `${entry.table}:${entry.id}`;\n const existingResolution = this.resolvedConflicts.get(resolutionKey);\n if (existingResolution) {\n if (__DEV__) {\n console.log('[Connector] Applying stored resolution for retry:', {\n table: entry.table,\n id: entry.id,\n key: resolutionKey,\n resolution: existingResolution\n });\n }\n // Remove from stored resolutions (single use)\n this.resolvedConflicts.delete(resolutionKey);\n switch (existingResolution.action) {\n case 'overwrite':\n // Proceed with upload (overwrite server)\n entriesToProcess.push(entry);\n break;\n case 'keep-server':\n // Discard local changes - mark for completion (removes from queue)\n entriesDiscarded.push(entry);\n break;\n case 'partial':\n // Only sync specified fields\n const partialEntry: CrudEntry = {\n ...entry,\n opData: this.filterFields(entry.opData ?? {}, existingResolution.fields)\n };\n entriesToProcess.push(partialEntry);\n break;\n }\n continue;\n }\n\n // Check for version column (cached)\n const hasVersion = await this.checkVersionColumn(entry.table, database);\n if (!hasVersion) {\n entriesToProcess.push(entry);\n continue;\n }\n\n // Get local and server versions\n const localVersion = await getLocalVersion(entry.table, entry.id, database);\n const schema = this.schemaRouter(entry.table);\n const serverVersion = await fetchServerVersion(entry.table, entry.id, schema, this.supabase);\n\n // If we can't get versions, skip conflict check and proceed\n if (localVersion === null || serverVersion === null) {\n entriesToProcess.push(entry);\n continue;\n }\n\n // Detect conflicts\n const conflictResult = await detectConflicts(entry.table, entry.id, localVersion, serverVersion, entry.opData ?? {}, this.supabase, this.conflictDetection);\n if (!conflictResult.hasConflict) {\n entriesToProcess.push(entry);\n continue;\n }\n\n // Emit conflict event to bus if available\n this.conflictBus?.emitConflict(conflictResult);\n\n // Handle conflict with handler if available\n if (this.conflictHandler) {\n const resolution = await this.conflictHandler.onConflict(conflictResult);\n if (resolution === null) {\n // Queue for UI - skip this entry, leave in queue for later resolution\n entriesQueuedForUI.push(entry);\n if (__DEV__) {\n console.log('[Connector] Conflict queued for UI resolution:', {\n table: entry.table,\n id: entry.id,\n conflicts: conflictResult.conflicts.map(c => c.field)\n });\n }\n continue;\n }\n switch (resolution.action) {\n case 'overwrite':\n // Proceed with upload (overwrite server)\n entriesToProcess.push(entry);\n break;\n case 'keep-server':\n // Discard local changes - mark for completion (removes from queue)\n entriesDiscarded.push(entry);\n if (__DEV__) {\n console.log('[Connector] Conflict resolved with keep-server, discarding local changes:', {\n table: entry.table,\n id: entry.id\n });\n }\n break;\n case 'partial':\n // Only sync specified fields\n const partialEntry: CrudEntry = {\n ...entry,\n opData: this.filterFields(entry.opData ?? {}, resolution.fields)\n };\n entriesToProcess.push(partialEntry);\n\n // Track this partial resolution to re-emit remaining conflicts after sync\n partialResolutions.push({\n originalConflict: conflictResult,\n syncedFields: resolution.fields\n });\n break;\n }\n } else {\n // No handler - log conflict and proceed with upload\n console.warn('[Connector] Conflict detected but no handler:', {\n table: entry.table,\n id: entry.id,\n conflicts: conflictResult.conflicts\n });\n entriesToProcess.push(entry);\n }\n }\n\n // If any entries are queued for UI resolution, we must NOT complete the transaction\n // This leaves those entries in the PowerSync queue for later resolution\n if (entriesQueuedForUI.length > 0) {\n if (__DEV__) {\n console.log('[Connector] Entries queued for UI resolution, leaving in queue:', {\n queuedForUI: entriesQueuedForUI.length,\n discarded: entriesDiscarded.length,\n toProcess: entriesToProcess.length\n });\n }\n // Fire onTransactionComplete to notify listeners, but NOT onTransactionSuccess\n // because we're not completing the transaction. Entries remain in the queue.\n this.onTransactionComplete?.(entriesQueuedForUI);\n return;\n }\n\n // If all entries were discarded (keep-server) with none to process,\n // still complete the transaction to remove them from the queue\n if (entriesToProcess.length === 0 && entriesDiscarded.length > 0) {\n if (__DEV__) {\n console.log('[Connector] All entries resolved with keep-server, completing transaction to discard local changes');\n }\n try {\n await transaction.complete();\n // Fire success (entries were \"successfully\" discarded)\n this.onTransactionSuccess?.(entriesDiscarded);\n this.onTransactionComplete?.(entriesDiscarded);\n } catch (error) {\n const classified = classifySupabaseError(error);\n this.onTransactionFailure?.(entriesDiscarded, error instanceof Error ? error : new Error(String(error)), classified);\n throw error;\n }\n return;\n }\n\n // Process remaining entries with retry logic\n // Track entries that failed critically (permanent errors that should stop the transaction)\n const criticalFailures: {\n entry: CrudEntry;\n error: Error;\n }[] = [];\n const successfulEntries: CrudEntry[] = [];\n for (const entry of entriesToProcess) {\n if (__DEV__) {\n console.log('[Connector] Processing CRUD entry with retry:', {\n table: entry.table,\n op: entry.op,\n id: entry.id,\n opData: entry.opData\n });\n }\n try {\n await this.processWithRetry(entry);\n successfulEntries.push(entry);\n } catch (error) {\n // Only processWithRetry throws for permanent/critical failures\n // Transient failures are added to the failed store and don't throw\n criticalFailures.push({\n entry,\n error: error instanceof Error ? error : new Error(String(error))\n });\n }\n }\n\n // If there are critical failures, we need to handle them appropriately\n if (criticalFailures.length > 0) {\n const firstFailure = criticalFailures[0];\n const classified = classifySupabaseError(firstFailure.error);\n\n // Always log to console for debugging\n console.error('[PowerSync Connector] Critical upload failure:', {\n errorMessage: firstFailure.error.message,\n classified,\n isPermanent: classified.isPermanent,\n criticalCount: criticalFailures.length,\n successCount: successfulEntries.length,\n entries: criticalFailures.map(f => ({\n table: f.entry.table,\n op: f.entry.op,\n id: f.entry.id\n }))\n });\n this.logger?.error('[Connector] Critical upload failure:', {\n error: firstFailure.error,\n classified,\n entries: criticalFailures.map(f => ({\n table: f.entry.table,\n op: f.entry.op,\n id: f.entry.id\n }))\n });\n\n // Notify failure with classification\n this.onTransactionFailure?.(criticalFailures.map(f => f.entry), firstFailure.error, classified);\n\n // Re-throw for PowerSync's native retry mechanism only for critical failures\n throw firstFailure.error;\n }\n if (__DEV__) {\n console.log('[Connector] All CRUD entries processed, completing transaction...');\n }\n try {\n await transaction.complete();\n if (__DEV__) {\n console.log('[Connector] Transaction completed successfully:', {\n entriesCount: successfulEntries.length,\n discardedCount: entriesDiscarded.length\n });\n }\n\n // After partial sync completes, re-emit conflicts for remaining fields\n if (this.conflictBus && partialResolutions.length > 0) {\n for (const {\n originalConflict,\n syncedFields\n } of partialResolutions) {\n const syncedFieldSet = new Set(syncedFields);\n\n // Find conflicts that weren't synced\n const remainingConflicts = originalConflict.conflicts.filter(c => !syncedFieldSet.has(c.field));\n if (remainingConflicts.length > 0) {\n if (__DEV__) {\n console.log('[Connector] Re-emitting conflict for remaining fields:', {\n table: originalConflict.table,\n recordId: originalConflict.recordId,\n syncedFields,\n remainingConflictFields: remainingConflicts.map(c => c.field)\n });\n }\n\n // Emit new conflict for remaining fields\n this.conflictBus.emitConflict({\n ...originalConflict,\n conflicts: remainingConflicts,\n // All remaining are conflicts now - clear nonConflictingChanges since\n // the non-conflicting ones were already synced in the partial resolution\n nonConflictingChanges: []\n });\n }\n }\n }\n\n // Notify success\n this.onTransactionSuccess?.(successfulEntries);\n\n // Notify completion (after transaction.complete() succeeds)\n this.onTransactionComplete?.(successfulEntries);\n } catch (error) {\n const classified = classifySupabaseError(error);\n\n // Always log to console for debugging\n console.error('[PowerSync Connector] Transaction completion FAILED:', {\n errorMessage: error instanceof Error ? error.message : String(error),\n errorKeys: error && typeof error === 'object' ? Object.keys(error) : [],\n errorObject: JSON.stringify(error, null, 2),\n classified,\n isPermanent: classified.isPermanent,\n entries: successfulEntries.map(e => ({\n table: e.table,\n op: e.op,\n id: e.id\n }))\n });\n this.logger?.error('[Connector] Transaction completion error:', {\n error,\n classified,\n entries: successfulEntries.map(e => ({\n table: e.table,\n op: e.op,\n id: e.id\n }))\n });\n\n // Notify failure with classification\n this.onTransactionFailure?.(successfulEntries, error instanceof Error ? error : new Error(String(error)), classified);\n\n // Re-throw for PowerSync's native retry mechanism\n throw error;\n }\n }\n\n /**\n * Process a transaction without conflict detection.\n * Used when conflict detection is disabled.\n */\n private async processTransaction(transaction: {\n crud: CrudEntry[];\n complete: () => Promise<void>;\n }, _database: AbstractPowerSyncDatabase): Promise<void> {\n // Track entries that failed critically (permanent errors that should stop the transaction)\n const criticalFailures: {\n entry: CrudEntry;\n error: Error;\n }[] = [];\n const successfulEntries: CrudEntry[] = [];\n for (const entry of transaction.crud) {\n if (__DEV__) {\n console.log('[Connector] Processing CRUD entry with retry:', {\n table: entry.table,\n op: entry.op,\n id: entry.id,\n opData: entry.opData\n });\n }\n try {\n await this.processWithRetry(entry);\n successfulEntries.push(entry);\n } catch (error) {\n // Only processWithRetry throws for permanent/critical failures\n // Transient failures are added to the failed store and don't throw\n criticalFailures.push({\n entry,\n error: error instanceof Error ? error : new Error(String(error))\n });\n }\n }\n\n // If there are critical failures, we need to handle them appropriately\n if (criticalFailures.length > 0) {\n const firstFailure = criticalFailures[0];\n const classified = classifySupabaseError(firstFailure.error);\n\n // Always log to console for debugging\n console.error('[PowerSync Connector] Critical upload failure:', {\n errorMessage: firstFailure.error.message,\n classified,\n isPermanent: classified.isPermanent,\n criticalCount: criticalFailures.length,\n successCount: successfulEntries.length,\n entries: criticalFailures.map(f => ({\n table: f.entry.table,\n op: f.entry.op,\n id: f.entry.id\n }))\n });\n this.logger?.error('[Connector] Critical upload failure:', {\n error: firstFailure.error,\n classified,\n entries: criticalFailures.map(f => ({\n table: f.entry.table,\n op: f.entry.op,\n id: f.entry.id\n }))\n });\n\n // Notify failure with classification\n this.onTransactionFailure?.(criticalFailures.map(f => f.entry), firstFailure.error, classified);\n\n // Re-throw for PowerSync's native retry mechanism only for critical failures\n throw firstFailure.error;\n }\n if (__DEV__) {\n console.log('[Connector] All CRUD entries processed, completing transaction...');\n }\n try {\n await transaction.complete();\n if (__DEV__) {\n console.log('[Connector] Transaction completed successfully:', {\n entriesCount: successfulEntries.length\n });\n }\n\n // Notify success\n this.onTransactionSuccess?.(successfulEntries);\n\n // Notify completion (after transaction.complete() succeeds)\n this.onTransactionComplete?.(successfulEntries);\n } catch (error) {\n const classified = classifySupabaseError(error);\n\n // Always log to console for debugging\n console.error('[PowerSync Connector] Transaction completion FAILED:', {\n errorMessage: error instanceof Error ? error.message : String(error),\n errorKeys: error && typeof error === 'object' ? Object.keys(error) : [],\n errorObject: JSON.stringify(error, null, 2),\n classified,\n isPermanent: classified.isPermanent,\n entries: successfulEntries.map(e => ({\n table: e.table,\n op: e.op,\n id: e.id\n }))\n });\n this.logger?.error('[Connector] Transaction completion error:', {\n error,\n classified,\n entries: successfulEntries.map(e => ({\n table: e.table,\n op: e.op,\n id: e.id\n }))\n });\n\n // Notify failure with classification\n this.onTransactionFailure?.(successfulEntries, error instanceof Error ? error : new Error(String(error)), classified);\n\n // Re-throw for PowerSync's native retry mechanism\n throw error;\n }\n }\n\n /**\n * Check if a table has a _version column (cached).\n */\n private async checkVersionColumn(table: string, db: AbstractPowerSyncDatabase): Promise<boolean> {\n if (this.versionColumnCache.has(table)) {\n return this.versionColumnCache.get(table)!;\n }\n const hasVersion = await hasVersionColumn(table, db);\n this.versionColumnCache.set(table, hasVersion);\n return hasVersion;\n }\n\n /**\n * Filter opData to only include specified fields.\n * Used for partial sync resolution.\n */\n private filterFields(opData: Record<string, unknown>, fields: string[]): Record<string, unknown> {\n const fieldSet = new Set(fields);\n const filtered: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(opData)) {\n if (fieldSet.has(key)) {\n filtered[key] = value;\n }\n }\n return filtered;\n }\n\n /**\n * Process a single CRUD operation.\n *\n * UUID-native tables (public schema, post-migration) use `id` as the UUID column.\n * Core schema tables (Profile, Comment, CommentSection) still use a separate `uuid` column.\n */\n /**\n * Process a single CRUD operation.\n *\n * All synced tables use `id` as their UUID primary key column.\n */\n private async processCrudEntry(entry: CrudEntry): Promise<void> {\n const table = entry.table;\n const id = entry.id; // PowerSync sends UUID as the entry.id\n const schema = this.schemaRouter(table);\n\n // Try custom handler first\n if (this.crudHandler) {\n let handled = false;\n switch (entry.op) {\n case UpdateType.PUT:\n handled = (await this.crudHandler.handlePut?.(entry, this.supabase, schema)) ?? false;\n break;\n case UpdateType.PATCH:\n handled = (await this.crudHandler.handlePatch?.(entry, this.supabase, schema)) ?? false;\n break;\n case UpdateType.DELETE:\n handled = (await this.crudHandler.handleDelete?.(entry, this.supabase, schema)) ?? false;\n break;\n }\n if (handled) {\n this.logger?.debug(`[Connector] Custom handler processed ${entry.op} for ${schema}.${table}`);\n return;\n }\n }\n\n // Default behavior\n // Get the correct Supabase query builder for this table's schema\n const query = schema === 'public' ? this.supabase.from(table) : (this.supabase.schema(schema) as unknown as ReturnType<typeof this.supabase.schema>).from(table);\n switch (entry.op) {\n case UpdateType.PUT:\n if (__DEV__) {\n console.log('[Connector] Executing PUT/UPSERT:', {\n schema,\n table,\n id,\n data: {\n id,\n ...entry.opData\n }\n });\n }\n // Insert/upsert using id column\n const {\n data: upsertData,\n error: upsertError\n } = await query.upsert({\n id,\n ...entry.opData\n }, {\n onConflict: 'id'\n }).select();\n if (upsertError) {\n if (__DEV__) {\n console.error('[Connector] PUT/UPSERT FAILED:', {\n schema,\n table,\n id,\n error: upsertError,\n errorMessage: upsertError.message,\n errorCode: upsertError.code,\n errorDetails: upsertError.details,\n errorHint: upsertError.hint\n });\n }\n throw new Error(`Upsert failed for ${schema}.${table}: ${upsertError.message}`);\n }\n if (__DEV__) {\n console.log('[Connector] PUT/UPSERT SUCCESS:', {\n schema,\n table,\n id,\n responseData: upsertData\n });\n }\n break;\n case UpdateType.PATCH:\n if (__DEV__) {\n console.log('[Connector] Executing PATCH/UPDATE:', {\n schema,\n table,\n id,\n opData: entry.opData\n });\n }\n // Update by id column\n const {\n data: updateData,\n error: updateError\n } = await query.update(entry.opData).eq('id', id).select();\n if (updateError) {\n if (__DEV__) {\n console.error('[Connector] PATCH/UPDATE FAILED:', {\n schema,\n table,\n id,\n error: updateError,\n errorMessage: updateError.message,\n errorCode: updateError.code,\n errorDetails: updateError.details,\n errorHint: updateError.hint\n });\n }\n throw new Error(`Update failed for ${schema}.${table}: ${updateError.message}`);\n }\n if (__DEV__) {\n console.log('[Connector] PATCH/UPDATE SUCCESS:', {\n schema,\n table,\n id,\n responseData: updateData\n });\n }\n break;\n case UpdateType.DELETE:\n if (__DEV__) {\n console.log('[Connector] Executing DELETE:', {\n schema,\n table,\n id\n });\n }\n // Delete by id column\n const {\n data: deleteData,\n error: deleteError\n } = await query.delete().eq('id', id).select();\n if (deleteError) {\n if (__DEV__) {\n console.error('[Connector] DELETE FAILED:', {\n schema,\n table,\n id,\n error: deleteError,\n errorMessage: deleteError.message,\n errorCode: deleteError.code,\n errorDetails: deleteError.details,\n errorHint: deleteError.hint\n });\n }\n throw new Error(`Delete failed for ${schema}.${table}: ${deleteError.message}`);\n }\n if (__DEV__) {\n console.log('[Connector] DELETE SUCCESS:', {\n schema,\n table,\n id,\n responseData: deleteData\n });\n }\n break;\n }\n this.logger?.debug(`[Connector] Processed ${entry.op} for ${schema}.${table} (id: ${id})`);\n }\n}"],"mappings":"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","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 { 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 +0,0 @@
1
- {"version":3,"sources":["../src/core/constants.ts"],"sourcesContent":["/**\n * Constants for @pol-studios/powersync\n *\n * This module contains all configurable constants with sensible defaults.\n * These can be overridden via configuration.\n */\n\n// ─── Storage Keys ────────────────────────────────────────────────────────────\n\n/** Storage key prefix for all PowerSync-related keys */\nexport const STORAGE_KEY_PREFIX = '@pol-studios/powersync';\n\n/** Storage key for PowerSync enabled state */\nexport const STORAGE_KEY_ENABLED = `${STORAGE_KEY_PREFIX}-enabled`;\n\n/** Storage key for PowerSync paused state */\nexport const STORAGE_KEY_PAUSED = `${STORAGE_KEY_PREFIX}-paused`;\n\n/** Storage key for persisted sync metrics */\nexport const STORAGE_KEY_METRICS = `${STORAGE_KEY_PREFIX}-metrics`;\n\n/** Storage key for attachment queue settings */\nexport const STORAGE_KEY_ATTACHMENT_SETTINGS = `${STORAGE_KEY_PREFIX}-attachment-settings`;\n\n/** Storage key for sync mode */\nexport const STORAGE_KEY_SYNC_MODE = `${STORAGE_KEY_PREFIX}-sync-mode`;\n\n/** Default sync mode */\nexport const DEFAULT_SYNC_MODE = 'push-pull' as const;\n\n// ─── Health Check Configuration ──────────────────────────────────────────────\n\n/** Interval between health checks in milliseconds */\nexport const HEALTH_CHECK_INTERVAL_MS = 30_000; // 30 seconds\n\n/** Timeout for individual health check queries */\nexport const HEALTH_CHECK_TIMEOUT_MS = 5_000; // 5 seconds\n\n/** Latency threshold above which connection is considered degraded */\nexport const LATENCY_DEGRADED_THRESHOLD_MS = 1_000; // 1 second\n\n/** Number of consecutive failures before marking as disconnected */\nexport const MAX_CONSECUTIVE_FAILURES = 2;\n\n// ─── Storage Quota Thresholds ────────────────────────────────────────────────\n\n/** Warning threshold for device free space (default: 100MB) */\nexport const STORAGE_WARNING_THRESHOLD = 100 * 1024 * 1024; // 100 MB\n\n/** Critical threshold for device free space (default: 50MB) */\nexport const STORAGE_CRITICAL_THRESHOLD = 50 * 1024 * 1024; // 50 MB\n\n// ─── Attachment Queue Configuration ──────────────────────────────────────────\n\n/** Default maximum cache size for attachments */\nexport const DEFAULT_ATTACHMENT_CACHE_SIZE = 5 * 1024 * 1024 * 1024; // 5 GB\n\n/** Default number of concurrent attachment downloads */\nexport const DEFAULT_ATTACHMENT_CONCURRENCY = 50;\n\n/** Delay between retry passes for attachment downloads */\nexport const ATTACHMENT_RETRY_DELAY_MS = 5_000; // 5 seconds\n\n/** Timeout for individual attachment downloads */\nexport const ATTACHMENT_DOWNLOAD_TIMEOUT_MS = 60_000; // 60 seconds\n\n/** Default image compression quality (0.0 to 1.0) */\nexport const DEFAULT_COMPRESSION_QUALITY = 0.7; // 70%\n\n/** Maximum image width before resizing */\nexport const COMPRESSION_MAX_WIDTH = 2048;\n\n/** Skip compression for files smaller than this */\nexport const COMPRESSION_SKIP_SIZE_BYTES = 100_000; // 100 KB\n\n/** Skip compression if already smaller than this */\nexport const COMPRESSION_TARGET_SIZE_BYTES = 300_000; // 300 KB\n\n/** Stop downloads at this percentage of cache limit to leave headroom */\nexport const DOWNLOAD_STOP_THRESHOLD = 0.95; // 95%\n\n/** Trigger eviction at this percentage of cache limit */\nexport const EVICTION_TRIGGER_THRESHOLD = 1.0; // 100%\n\n// ─── Sync Configuration ──────────────────────────────────────────────────────\n\n/** Default sync interval for periodic sync mode */\nexport const DEFAULT_SYNC_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes\n\n/** Debounce time for status notifications */\nexport const STATUS_NOTIFY_THROTTLE_MS = 500;\n\n/** Cache TTL for stats queries */\nexport const STATS_CACHE_TTL_MS = 500;\n\n// ─── Retry Configuration ─────────────────────────────────────────────────────\n\n/** Default maximum retry attempts */\nexport const DEFAULT_MAX_RETRY_ATTEMPTS = 3;\n\n/** Default base delay for retry backoff */\nexport const DEFAULT_RETRY_BASE_DELAY_MS = 1_000; // 1 second\n\n/** Default maximum delay between retries */\nexport const DEFAULT_RETRY_MAX_DELAY_MS = 30_000; // 30 seconds\n\n/** Default backoff multiplier */\nexport const DEFAULT_RETRY_BACKOFF_MULTIPLIER = 2;"],"mappings":";AAUO,IAAM,qBAAqB;AAG3B,IAAM,sBAAsB,GAAG,kBAAkB;AAGjD,IAAM,qBAAqB,GAAG,kBAAkB;AAGhD,IAAM,sBAAsB,GAAG,kBAAkB;AAGjD,IAAM,kCAAkC,GAAG,kBAAkB;AAG7D,IAAM,wBAAwB,GAAG,kBAAkB;AAGnD,IAAM,oBAAoB;AAK1B,IAAM,2BAA2B;AAGjC,IAAM,0BAA0B;AAGhC,IAAM,gCAAgC;AAGtC,IAAM,2BAA2B;AAKjC,IAAM,4BAA4B,MAAM,OAAO;AAG/C,IAAM,6BAA6B,KAAK,OAAO;AAK/C,IAAM,gCAAgC,IAAI,OAAO,OAAO;AAGxD,IAAM,iCAAiC;AAGvC,IAAM,4BAA4B;AAGlC,IAAM,iCAAiC;AAGvC,IAAM,8BAA8B;AAGpC,IAAM,wBAAwB;AAG9B,IAAM,8BAA8B;AAGpC,IAAM,gCAAgC;AAGtC,IAAM,0BAA0B;AAGhC,IAAM,6BAA6B;AAKnC,IAAM,2BAA2B,IAAI,KAAK;AAG1C,IAAM,4BAA4B;AAGlC,IAAM,qBAAqB;AAK3B,IAAM,6BAA6B;AAGnC,IAAM,8BAA8B;AAGpC,IAAM,6BAA6B;AAGnC,IAAM,mCAAmC;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/core/errors.ts"],"sourcesContent":["/**\n * Custom Error Classes for @pol-studios/powersync\n *\n * This module provides specialized error classes for different failure scenarios.\n */\n\nimport type { SyncErrorType, ClassifiedError, SyncError, CrudEntry } from './types';\n\n/**\n * Base error class for PowerSync-related errors\n */\nexport class PowerSyncError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'PowerSyncError';\n // Maintains proper stack trace for where our error was thrown (only in V8)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, PowerSyncError);\n }\n }\n}\n\n/**\n * Error thrown when PowerSync initialization fails\n */\nexport class InitializationError extends PowerSyncError {\n constructor(message: string, public readonly cause?: Error) {\n super(message);\n this.name = 'InitializationError';\n }\n}\n\n/**\n * Error thrown when a sync operation fails\n */\nexport class SyncOperationError extends PowerSyncError {\n constructor(message: string, public readonly errorType: SyncErrorType, public readonly cause?: Error) {\n super(message);\n this.name = 'SyncOperationError';\n }\n\n /**\n * Whether this error can be automatically retried\n */\n get isRetryable(): boolean {\n return this.errorType === 'network' || this.errorType === 'server';\n }\n\n /**\n * Get a user-friendly error message\n */\n get userFriendlyMessage(): string {\n switch (this.errorType) {\n case 'network':\n return 'Unable to connect. Check your internet connection.';\n case 'auth':\n return 'Session expired. Please sign in again.';\n case 'server':\n return 'Server is temporarily unavailable. Try again later.';\n case 'conflict':\n return 'Your changes conflict with recent updates.';\n case 'quota':\n return 'Device storage is full. Free up space to continue.';\n default:\n return 'An unexpected error occurred. Please try again.';\n }\n }\n}\n\n/**\n * Error thrown when a connector operation fails\n */\nexport class ConnectorError extends PowerSyncError {\n constructor(message: string, public readonly operation: 'fetchCredentials' | 'uploadData', public readonly cause?: Error) {\n super(message);\n this.name = 'ConnectorError';\n }\n}\n\n/**\n * Error thrown when an attachment operation fails\n */\nexport class AttachmentError extends PowerSyncError {\n constructor(message: string, public readonly attachmentId: string, public readonly operation: 'download' | 'compress' | 'delete' | 'evict', public readonly cause?: Error) {\n super(message);\n this.name = 'AttachmentError';\n }\n}\n\n/**\n * Error thrown when the platform adapter is missing required functionality\n */\nexport class PlatformAdapterError extends PowerSyncError {\n constructor(message: string, public readonly missingFeature: string) {\n super(message);\n this.name = 'PlatformAdapterError';\n }\n}\n\n/**\n * Error thrown when configuration is invalid\n */\nexport class ConfigurationError extends PowerSyncError {\n constructor(message: string, public readonly configKey?: string) {\n super(message);\n this.name = 'ConfigurationError';\n }\n}\n\n// ─── Error Classification Utilities ──────────────────────────────────────────\n\n/** Pattern definitions for error classification */\nconst ERROR_PATTERNS: Record<SyncErrorType, RegExp> = {\n network: /network|fetch|econnrefused|etimedout|offline/i,\n auth: /401|403|auth|token|unauthorized|forbidden/i,\n server: /500|502|503|504|internal server/i,\n conflict: /conflict|concurrent|version/i,\n validation: /validation|constraint|invalid|required/i,\n quota: /quota|storage|enospc|disk/i,\n unknown: /.*/\n};\n\n/**\n * Classify an error into a SyncErrorType based on its message\n *\n * @param error - The error to classify\n * @returns The classified error type\n */\nexport function classifyError(error: Error): SyncErrorType {\n const message = error.message || '';\n\n // Check patterns in priority order (more specific first)\n if (ERROR_PATTERNS.auth.test(message)) return 'auth';\n if (ERROR_PATTERNS.server.test(message)) return 'server';\n if (ERROR_PATTERNS.network.test(message)) return 'network';\n if (ERROR_PATTERNS.conflict.test(message)) return 'conflict';\n if (ERROR_PATTERNS.validation.test(message)) return 'validation';\n if (ERROR_PATTERNS.quota.test(message)) return 'quota';\n return 'unknown';\n}\n\n/**\n * Create a SyncOperationError from a regular Error\n *\n * @param error - The original error\n * @param message - Optional custom message\n * @returns A classified SyncOperationError\n */\nexport function toSyncOperationError(error: Error, message?: string): SyncOperationError {\n const errorType = classifyError(error);\n return new SyncOperationError(message || error.message, errorType, error);\n}\n\n// ─── Supabase/PostgreSQL Error Classification ────────────────────────────────\n\n/**\n * PostgreSQL error code ranges and their meanings.\n * See: https://www.postgresql.org/docs/current/errcodes-appendix.html\n */\nconst PG_ERROR_CODES = {\n // Class 23 - Integrity Constraint Violation (permanent - data issue)\n UNIQUE_VIOLATION: '23505',\n FOREIGN_KEY_VIOLATION: '23503',\n NOT_NULL_VIOLATION: '23502',\n CHECK_VIOLATION: '23514',\n // Class 42 - Syntax Error or Access Rule Violation (permanent - schema issue)\n UNDEFINED_TABLE: '42P01',\n UNDEFINED_COLUMN: '42703',\n INSUFFICIENT_PRIVILEGE: '42501',\n // Class 08 - Connection Exception (transient)\n CONNECTION_FAILURE: '08006',\n CONNECTION_DOES_NOT_EXIST: '08003',\n // Class 53 - Insufficient Resources (transient)\n OUT_OF_MEMORY: '53200',\n DISK_FULL: '53100',\n // Class 40 - Transaction Rollback (transient)\n SERIALIZATION_FAILURE: '40001',\n DEADLOCK_DETECTED: '40P01'\n} as const;\n\n/**\n * User-friendly messages for PostgreSQL error codes\n */\nconst PG_ERROR_MESSAGES: Record<string, string> = {\n [PG_ERROR_CODES.UNIQUE_VIOLATION]: 'This record already exists or conflicts with existing data.',\n [PG_ERROR_CODES.FOREIGN_KEY_VIOLATION]: 'This record references data that no longer exists.',\n [PG_ERROR_CODES.NOT_NULL_VIOLATION]: 'A required field is missing.',\n [PG_ERROR_CODES.CHECK_VIOLATION]: 'The data does not meet validation requirements.',\n [PG_ERROR_CODES.UNDEFINED_TABLE]: 'The database table does not exist.',\n [PG_ERROR_CODES.UNDEFINED_COLUMN]: 'A database column does not exist.',\n [PG_ERROR_CODES.INSUFFICIENT_PRIVILEGE]: 'You do not have permission to perform this action.'\n};\n\n/**\n * Extract HTTP status code from an error object.\n *\n * Checks for common status code properties and falls back to pattern matching\n * in the error message for 4xx/5xx codes.\n */\nfunction extractHttpStatusCode(error: unknown): number | undefined {\n if (!error || typeof error !== 'object') return undefined;\n const err = error as Record<string, unknown>;\n\n // Direct properties\n if (typeof err.status === 'number') return err.status;\n if (typeof err.statusCode === 'number') return err.statusCode;\n\n // Check nested error object (Supabase format)\n if (err.error && typeof err.error === 'object') {\n const nested = err.error as Record<string, unknown>;\n if (typeof nested.status === 'number') return nested.status;\n if (typeof nested.statusCode === 'number') return nested.statusCode;\n }\n\n // Pattern match in message\n const message = String(err.message || '');\n const match = message.match(/\\b(4\\d{2}|5\\d{2})\\b/);\n if (match) return parseInt(match[1], 10);\n return undefined;\n}\n\n/**\n * Extract PostgreSQL error code from a Supabase error.\n *\n * Supabase errors from PostgREST typically include the PostgreSQL error code\n * in the error object or message.\n */\nfunction extractPgCode(error: unknown): string | undefined {\n if (!error || typeof error !== 'object') return undefined;\n const err = error as Record<string, unknown>;\n\n // Supabase error format: { code: \"PGRST...\", details: \"...\", hint: \"...\", message: \"...\" }\n // PostgreSQL error format: { code: \"23505\", ... }\n if (typeof err.code === 'string') {\n // Check if it's a direct PostgreSQL code (5 chars, starts with digit)\n if (/^\\d{5}$/.test(err.code)) {\n return err.code;\n }\n // Extract from PGRST format or message\n const match = err.code.match(/\\d{5}/) || String(err.message || '').match(/\\d{5}/);\n if (match) return match[0];\n }\n\n // Check in details or hint\n if (typeof err.details === 'string') {\n const match = err.details.match(/\\b(\\d{5})\\b/);\n if (match) return match[1];\n }\n return undefined;\n}\n\n/**\n * User-friendly messages for PostgREST error codes (PGRST*).\n * See: https://postgrest.org/en/stable/references/errors.html\n */\nfunction getPostgRESTMessage(code: string, fallback: string): string {\n const messages: Record<string, string> = {\n 'PGRST116': 'Record not found or query returned no results.',\n 'PGRST204': 'Column not found in the database.',\n 'PGRST301': 'Row-level security prevented this operation.',\n 'PGRST302': 'The requested operation is not allowed.'\n };\n return messages[code] || `Database error: ${fallback}`;\n}\n\n/**\n * Classify a Supabase/PostgreSQL error into a structured result.\n *\n * This function analyzes errors from Supabase operations and determines:\n * - The error type category\n * - Whether the error is permanent (won't be fixed by retry)\n * - Whether it's a conflict with existing data\n * - A user-friendly message\n *\n * @param error - The error from a Supabase operation\n * @returns Classified error information\n *\n * @example\n * ```typescript\n * try {\n * await supabase.from('users').insert({ email: 'duplicate@test.com' });\n * } catch (error) {\n * const classified = classifySupabaseError(error);\n * if (classified.isPermanent) {\n * // Show error to user - retry won't help\n * }\n * }\n * ```\n */\nexport function classifySupabaseError(error: unknown): ClassifiedError {\n const pgCode = extractPgCode(error);\n const httpStatusCode = extractHttpStatusCode(error);\n const errorMessage = error instanceof Error ? error.message : String(error);\n\n // Default result\n let result: ClassifiedError = {\n type: 'unknown',\n isPermanent: false,\n isConflict: false,\n pgCode,\n userMessage: 'An unexpected error occurred. Please try again.'\n };\n\n // Check for PostgREST error codes (PGRST*)\n if (error && typeof error === 'object') {\n const err = error as Record<string, unknown>;\n const code = String(err.code || '');\n if (code.startsWith('PGRST')) {\n result.type = 'validation';\n result.isPermanent = true;\n result.userMessage = getPostgRESTMessage(code, errorMessage);\n return result;\n }\n }\n\n // First, check HTTP status code for quick classification\n if (httpStatusCode) {\n // 4xx client errors (except 401/403 which are auth errors handled separately)\n if (httpStatusCode >= 400 && httpStatusCode < 500 && httpStatusCode !== 401 && httpStatusCode !== 403) {\n result.type = 'validation';\n result.isPermanent = true;\n result.userMessage = 'The request was rejected. Please check your data.';\n return result;\n }\n\n // 5xx server errors (transient)\n if (httpStatusCode >= 500) {\n result.type = 'server';\n result.isPermanent = false;\n result.userMessage = 'Server temporarily unavailable. Will retry.';\n return result;\n }\n }\n\n // Second, check PostgreSQL error code (most reliable)\n if (pgCode) {\n // Class 23 - Integrity Constraint Violations (permanent)\n if (pgCode.startsWith('23')) {\n result.type = pgCode === PG_ERROR_CODES.UNIQUE_VIOLATION ? 'conflict' : 'validation';\n result.isPermanent = true;\n result.isConflict = pgCode === PG_ERROR_CODES.UNIQUE_VIOLATION;\n result.userMessage = PG_ERROR_MESSAGES[pgCode] || 'Data validation failed.';\n return result;\n }\n\n // Class 42 - Syntax/Access Errors (permanent - schema issue)\n if (pgCode.startsWith('42')) {\n result.type = pgCode === PG_ERROR_CODES.INSUFFICIENT_PRIVILEGE ? 'auth' : 'validation';\n result.isPermanent = true;\n result.userMessage = PG_ERROR_MESSAGES[pgCode] || 'Database schema error.';\n return result;\n }\n\n // Class 08 - Connection Exceptions (transient)\n if (pgCode.startsWith('08')) {\n result.type = 'network';\n result.isPermanent = false;\n result.userMessage = 'Connection lost. Will retry automatically.';\n return result;\n }\n\n // Class 53 - Insufficient Resources (transient, but may need action)\n if (pgCode.startsWith('53')) {\n result.type = 'quota';\n result.isPermanent = pgCode === PG_ERROR_CODES.DISK_FULL;\n result.userMessage = 'Server resources exhausted. Please try again later.';\n return result;\n }\n\n // Class 40 - Transaction Rollback (transient)\n if (pgCode.startsWith('40')) {\n result.type = 'conflict';\n result.isPermanent = false;\n result.isConflict = true;\n result.userMessage = 'Concurrent update detected. Retrying...';\n return result;\n }\n }\n\n // Fall back to pattern matching on error message\n const lowerMessage = errorMessage.toLowerCase();\n\n // Network errors (transient)\n if (/network|fetch|econnrefused|etimedout|offline|dns|socket/.test(lowerMessage)) {\n result.type = 'network';\n result.isPermanent = false;\n result.userMessage = 'Network error. Will retry when connected.';\n return result;\n }\n\n // Auth errors (may be permanent if token is invalid)\n if (/401|403|auth|token|unauthorized|forbidden|jwt|expired/.test(lowerMessage)) {\n result.type = 'auth';\n result.isPermanent = lowerMessage.includes('expired') || lowerMessage.includes('invalid');\n result.userMessage = 'Authentication error. Please sign in again.';\n return result;\n }\n\n // Client errors (permanent - request is malformed or rejected)\n if (/400|406|bad request|not acceptable|422|unprocessable/i.test(lowerMessage)) {\n result.type = 'validation';\n result.isPermanent = true;\n result.userMessage = 'The request was rejected. Please check your data.';\n return result;\n }\n\n // Server errors (transient)\n if (/500|502|503|504|internal server|service unavailable/.test(lowerMessage)) {\n result.type = 'server';\n result.isPermanent = false;\n result.userMessage = 'Server temporarily unavailable. Will retry.';\n return result;\n }\n\n // Constraint/validation errors in message (permanent)\n if (/duplicate|unique|constraint|violates|invalid|required/.test(lowerMessage)) {\n result.type = 'validation';\n result.isPermanent = true;\n result.isConflict = lowerMessage.includes('duplicate') || lowerMessage.includes('unique');\n result.userMessage = 'Data validation failed. Please check your input.';\n return result;\n }\n return result;\n}\n\n/**\n * Create a SyncError from a classified error result.\n */\nexport function createSyncError(classified: ClassifiedError, originalMessage: string): SyncError {\n return {\n type: classified.type,\n message: originalMessage,\n userMessage: classified.userMessage,\n timestamp: new Date(),\n pgCode: classified.pgCode,\n isPermanent: classified.isPermanent\n };\n}\n\n/**\n * Generate a unique ID for a failed transaction.\n * Uses a combination of timestamp and entry IDs to ensure uniqueness.\n */\nexport function generateFailureId(entries: CrudEntry[]): string {\n const timestamp = Date.now();\n const entryIds = entries.map(e => e.id).join('-');\n return `failure-${timestamp}-${entryIds.substring(0, 32)}`;\n}\n\n/**\n * Extract entity IDs from CRUD entries.\n * Includes both the PowerSync entry ID and the record's 'id' from opData if different.\n * This ensures we can match failures to entities regardless of which ID format is used.\n */\nexport function extractEntityIds(entries: CrudEntry[]): string[] {\n const ids: string[] = [];\n for (const entry of entries) {\n // Always include the PowerSync entry ID\n ids.push(entry.id);\n // Also include the record's 'id' field from opData if it exists and is different\n if (entry.opData?.id !== undefined && String(entry.opData.id) !== entry.id) {\n ids.push(String(entry.opData.id));\n }\n }\n return [...new Set(ids)];\n}\n\n/**\n * Extract unique table names from CRUD entries.\n */\nexport function extractTableNames(entries: CrudEntry[]): string[] {\n return [...new Set(entries.map(entry => entry.table))];\n}"],"mappings":";AAWO,IAAM,iBAAN,MAAM,wBAAuB,MAAM;AAAA,EACxC,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAEZ,QAAI,MAAM,mBAAmB;AAC3B,YAAM,kBAAkB,MAAM,eAAc;AAAA,IAC9C;AAAA,EACF;AACF;AAKO,IAAM,sBAAN,cAAkC,eAAe;AAAA,EACtD,YAAY,SAAiC,OAAe;AAC1D,UAAM,OAAO;AAD8B;AAE3C,SAAK,OAAO;AAAA,EACd;AACF;AAKO,IAAM,qBAAN,cAAiC,eAAe;AAAA,EACrD,YAAY,SAAiC,WAA0C,OAAe;AACpG,UAAM,OAAO;AAD8B;AAA0C;AAErF,SAAK,OAAO;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,cAAuB;AACzB,WAAO,KAAK,cAAc,aAAa,KAAK,cAAc;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,sBAA8B;AAChC,YAAQ,KAAK,WAAW;AAAA,MACtB,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT;AACE,eAAO;AAAA,IACX;AAAA,EACF;AACF;AAKO,IAAM,iBAAN,cAA6B,eAAe;AAAA,EACjD,YAAY,SAAiC,WAA8D,OAAe;AACxH,UAAM,OAAO;AAD8B;AAA8D;AAEzG,SAAK,OAAO;AAAA,EACd;AACF;AAKO,IAAM,kBAAN,cAA8B,eAAe;AAAA,EAClD,YAAY,SAAiC,cAAsC,WAAyE,OAAe;AACzK,UAAM,OAAO;AAD8B;AAAsC;AAAyE;AAE1J,SAAK,OAAO;AAAA,EACd;AACF;AAKO,IAAM,uBAAN,cAAmC,eAAe;AAAA,EACvD,YAAY,SAAiC,gBAAwB;AACnE,UAAM,OAAO;AAD8B;AAE3C,SAAK,OAAO;AAAA,EACd;AACF;AAKO,IAAM,qBAAN,cAAiC,eAAe;AAAA,EACrD,YAAY,SAAiC,WAAoB;AAC/D,UAAM,OAAO;AAD8B;AAE3C,SAAK,OAAO;AAAA,EACd;AACF;AAKA,IAAM,iBAAgD;AAAA,EACpD,SAAS;AAAA,EACT,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,SAAS;AACX;AAQO,SAAS,cAAc,OAA6B;AACzD,QAAM,UAAU,MAAM,WAAW;AAGjC,MAAI,eAAe,KAAK,KAAK,OAAO,EAAG,QAAO;AAC9C,MAAI,eAAe,OAAO,KAAK,OAAO,EAAG,QAAO;AAChD,MAAI,eAAe,QAAQ,KAAK,OAAO,EAAG,QAAO;AACjD,MAAI,eAAe,SAAS,KAAK,OAAO,EAAG,QAAO;AAClD,MAAI,eAAe,WAAW,KAAK,OAAO,EAAG,QAAO;AACpD,MAAI,eAAe,MAAM,KAAK,OAAO,EAAG,QAAO;AAC/C,SAAO;AACT;AASO,SAAS,qBAAqB,OAAc,SAAsC;AACvF,QAAM,YAAY,cAAc,KAAK;AACrC,SAAO,IAAI,mBAAmB,WAAW,MAAM,SAAS,WAAW,KAAK;AAC1E;AAQA,IAAM,iBAAiB;AAAA;AAAA,EAErB,kBAAkB;AAAA,EAClB,uBAAuB;AAAA,EACvB,oBAAoB;AAAA,EACpB,iBAAiB;AAAA;AAAA,EAEjB,iBAAiB;AAAA,EACjB,kBAAkB;AAAA,EAClB,wBAAwB;AAAA;AAAA,EAExB,oBAAoB;AAAA,EACpB,2BAA2B;AAAA;AAAA,EAE3B,eAAe;AAAA,EACf,WAAW;AAAA;AAAA,EAEX,uBAAuB;AAAA,EACvB,mBAAmB;AACrB;AAKA,IAAM,oBAA4C;AAAA,EAChD,CAAC,eAAe,gBAAgB,GAAG;AAAA,EACnC,CAAC,eAAe,qBAAqB,GAAG;AAAA,EACxC,CAAC,eAAe,kBAAkB,GAAG;AAAA,EACrC,CAAC,eAAe,eAAe,GAAG;AAAA,EAClC,CAAC,eAAe,eAAe,GAAG;AAAA,EAClC,CAAC,eAAe,gBAAgB,GAAG;AAAA,EACnC,CAAC,eAAe,sBAAsB,GAAG;AAC3C;AAQA,SAAS,sBAAsB,OAAoC;AACjE,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,MAAM;AAGZ,MAAI,OAAO,IAAI,WAAW,SAAU,QAAO,IAAI;AAC/C,MAAI,OAAO,IAAI,eAAe,SAAU,QAAO,IAAI;AAGnD,MAAI,IAAI,SAAS,OAAO,IAAI,UAAU,UAAU;AAC9C,UAAM,SAAS,IAAI;AACnB,QAAI,OAAO,OAAO,WAAW,SAAU,QAAO,OAAO;AACrD,QAAI,OAAO,OAAO,eAAe,SAAU,QAAO,OAAO;AAAA,EAC3D;AAGA,QAAM,UAAU,OAAO,IAAI,WAAW,EAAE;AACxC,QAAM,QAAQ,QAAQ,MAAM,qBAAqB;AACjD,MAAI,MAAO,QAAO,SAAS,MAAM,CAAC,GAAG,EAAE;AACvC,SAAO;AACT;AAQA,SAAS,cAAc,OAAoC;AACzD,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,MAAM;AAIZ,MAAI,OAAO,IAAI,SAAS,UAAU;AAEhC,QAAI,UAAU,KAAK,IAAI,IAAI,GAAG;AAC5B,aAAO,IAAI;AAAA,IACb;AAEA,UAAM,QAAQ,IAAI,KAAK,MAAM,OAAO,KAAK,OAAO,IAAI,WAAW,EAAE,EAAE,MAAM,OAAO;AAChF,QAAI,MAAO,QAAO,MAAM,CAAC;AAAA,EAC3B;AAGA,MAAI,OAAO,IAAI,YAAY,UAAU;AACnC,UAAM,QAAQ,IAAI,QAAQ,MAAM,aAAa;AAC7C,QAAI,MAAO,QAAO,MAAM,CAAC;AAAA,EAC3B;AACA,SAAO;AACT;AAMA,SAAS,oBAAoB,MAAc,UAA0B;AACnE,QAAM,WAAmC;AAAA,IACvC,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,EACd;AACA,SAAO,SAAS,IAAI,KAAK,mBAAmB,QAAQ;AACtD;AA0BO,SAAS,sBAAsB,OAAiC;AACrE,QAAM,SAAS,cAAc,KAAK;AAClC,QAAM,iBAAiB,sBAAsB,KAAK;AAClD,QAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAG1E,MAAI,SAA0B;AAAA,IAC5B,MAAM;AAAA,IACN,aAAa;AAAA,IACb,YAAY;AAAA,IACZ;AAAA,IACA,aAAa;AAAA,EACf;AAGA,MAAI,SAAS,OAAO,UAAU,UAAU;AACtC,UAAM,MAAM;AACZ,UAAM,OAAO,OAAO,IAAI,QAAQ,EAAE;AAClC,QAAI,KAAK,WAAW,OAAO,GAAG;AAC5B,aAAO,OAAO;AACd,aAAO,cAAc;AACrB,aAAO,cAAc,oBAAoB,MAAM,YAAY;AAC3D,aAAO;AAAA,IACT;AAAA,EACF;AAGA,MAAI,gBAAgB;AAElB,QAAI,kBAAkB,OAAO,iBAAiB,OAAO,mBAAmB,OAAO,mBAAmB,KAAK;AACrG,aAAO,OAAO;AACd,aAAO,cAAc;AACrB,aAAO,cAAc;AACrB,aAAO;AAAA,IACT;AAGA,QAAI,kBAAkB,KAAK;AACzB,aAAO,OAAO;AACd,aAAO,cAAc;AACrB,aAAO,cAAc;AACrB,aAAO;AAAA,IACT;AAAA,EACF;AAGA,MAAI,QAAQ;AAEV,QAAI,OAAO,WAAW,IAAI,GAAG;AAC3B,aAAO,OAAO,WAAW,eAAe,mBAAmB,aAAa;AACxE,aAAO,cAAc;AACrB,aAAO,aAAa,WAAW,eAAe;AAC9C,aAAO,cAAc,kBAAkB,MAAM,KAAK;AAClD,aAAO;AAAA,IACT;AAGA,QAAI,OAAO,WAAW,IAAI,GAAG;AAC3B,aAAO,OAAO,WAAW,eAAe,yBAAyB,SAAS;AAC1E,aAAO,cAAc;AACrB,aAAO,cAAc,kBAAkB,MAAM,KAAK;AAClD,aAAO;AAAA,IACT;AAGA,QAAI,OAAO,WAAW,IAAI,GAAG;AAC3B,aAAO,OAAO;AACd,aAAO,cAAc;AACrB,aAAO,cAAc;AACrB,aAAO;AAAA,IACT;AAGA,QAAI,OAAO,WAAW,IAAI,GAAG;AAC3B,aAAO,OAAO;AACd,aAAO,cAAc,WAAW,eAAe;AAC/C,aAAO,cAAc;AACrB,aAAO;AAAA,IACT;AAGA,QAAI,OAAO,WAAW,IAAI,GAAG;AAC3B,aAAO,OAAO;AACd,aAAO,cAAc;AACrB,aAAO,aAAa;AACpB,aAAO,cAAc;AACrB,aAAO;AAAA,IACT;AAAA,EACF;AAGA,QAAM,eAAe,aAAa,YAAY;AAG9C,MAAI,0DAA0D,KAAK,YAAY,GAAG;AAChF,WAAO,OAAO;AACd,WAAO,cAAc;AACrB,WAAO,cAAc;AACrB,WAAO;AAAA,EACT;AAGA,MAAI,wDAAwD,KAAK,YAAY,GAAG;AAC9E,WAAO,OAAO;AACd,WAAO,cAAc,aAAa,SAAS,SAAS,KAAK,aAAa,SAAS,SAAS;AACxF,WAAO,cAAc;AACrB,WAAO;AAAA,EACT;AAGA,MAAI,wDAAwD,KAAK,YAAY,GAAG;AAC9E,WAAO,OAAO;AACd,WAAO,cAAc;AACrB,WAAO,cAAc;AACrB,WAAO;AAAA,EACT;AAGA,MAAI,sDAAsD,KAAK,YAAY,GAAG;AAC5E,WAAO,OAAO;AACd,WAAO,cAAc;AACrB,WAAO,cAAc;AACrB,WAAO;AAAA,EACT;AAGA,MAAI,wDAAwD,KAAK,YAAY,GAAG;AAC9E,WAAO,OAAO;AACd,WAAO,cAAc;AACrB,WAAO,aAAa,aAAa,SAAS,WAAW,KAAK,aAAa,SAAS,QAAQ;AACxF,WAAO,cAAc;AACrB,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAKO,SAAS,gBAAgB,YAA6B,iBAAoC;AAC/F,SAAO;AAAA,IACL,MAAM,WAAW;AAAA,IACjB,SAAS;AAAA,IACT,aAAa,WAAW;AAAA,IACxB,WAAW,oBAAI,KAAK;AAAA,IACpB,QAAQ,WAAW;AAAA,IACnB,aAAa,WAAW;AAAA,EAC1B;AACF;AAMO,SAAS,kBAAkB,SAA8B;AAC9D,QAAM,YAAY,KAAK,IAAI;AAC3B,QAAM,WAAW,QAAQ,IAAI,OAAK,EAAE,EAAE,EAAE,KAAK,GAAG;AAChD,SAAO,WAAW,SAAS,IAAI,SAAS,UAAU,GAAG,EAAE,CAAC;AAC1D;AAOO,SAAS,iBAAiB,SAAgC;AAC/D,QAAM,MAAgB,CAAC;AACvB,aAAW,SAAS,SAAS;AAE3B,QAAI,KAAK,MAAM,EAAE;AAEjB,QAAI,MAAM,QAAQ,OAAO,UAAa,OAAO,MAAM,OAAO,EAAE,MAAM,MAAM,IAAI;AAC1E,UAAI,KAAK,OAAO,MAAM,OAAO,EAAE,CAAC;AAAA,IAClC;AAAA,EACF;AACA,SAAO,CAAC,GAAG,IAAI,IAAI,GAAG,CAAC;AACzB;AAKO,SAAS,kBAAkB,SAAgC;AAChE,SAAO,CAAC,GAAG,IAAI,IAAI,QAAQ,IAAI,WAAS,MAAM,KAAK,CAAC,CAAC;AACvD;","names":[]}