@pol-studios/powersync 1.0.30 → 1.0.33

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 (116) hide show
  1. package/dist/{CacheSettingsManager-uz-kbnRH.d.ts → CacheSettingsManager-0H_7thHW.d.ts} +21 -3
  2. package/dist/attachments/index.d.ts +30 -30
  3. package/dist/attachments/index.js +13 -4
  4. package/dist/{background-sync-CVR3PkFi.d.ts → background-sync-BujnI3IR.d.ts} +1 -1
  5. package/dist/{chunk-RE5HWLCB.js → chunk-2RDWLXJW.js} +322 -103
  6. package/dist/chunk-2RDWLXJW.js.map +1 -0
  7. package/dist/{chunk-P4HZA6ZT.js → chunk-4665ZSE5.js} +2 -2
  8. package/dist/chunk-4665ZSE5.js.map +1 -0
  9. package/dist/{chunk-XOY2CJ67.js → chunk-4F5B5CZ7.js} +3 -3
  10. package/dist/chunk-5WRI5ZAA.js +31 -0
  11. package/dist/{chunk-BC2SRII2.js → chunk-65A3SYJZ.js} +14 -1
  12. package/dist/chunk-65A3SYJZ.js.map +1 -0
  13. package/dist/chunk-6SZ64KCZ.js +755 -0
  14. package/dist/chunk-6SZ64KCZ.js.map +1 -0
  15. package/dist/{chunk-C2ACBYBZ.js → chunk-74TBHWJ4.js} +10 -96
  16. package/dist/{chunk-C2ACBYBZ.js.map → chunk-74TBHWJ4.js.map} +1 -1
  17. package/dist/chunk-ANXWYQEJ.js +1 -0
  18. package/dist/chunk-ANXWYQEJ.js.map +1 -0
  19. package/dist/{chunk-CAB26E6F.js → chunk-C4J4MLER.js} +29 -24
  20. package/dist/chunk-C4J4MLER.js.map +1 -0
  21. package/dist/{chunk-C5ODS3XH.js → chunk-EOW7JK7Q.js} +9 -16
  22. package/dist/chunk-EOW7JK7Q.js.map +1 -0
  23. package/dist/chunk-HRAVPIAZ.js +220 -0
  24. package/dist/chunk-HRAVPIAZ.js.map +1 -0
  25. package/dist/{chunk-XAEII4ZX.js → chunk-NUGQOTEM.js} +32 -4
  26. package/dist/chunk-NUGQOTEM.js.map +1 -0
  27. package/dist/chunk-OGUFUZSY.js +5415 -0
  28. package/dist/chunk-OGUFUZSY.js.map +1 -0
  29. package/dist/{chunk-JCGOZVWL.js → chunk-P4D6BQ4X.js} +115 -576
  30. package/dist/chunk-P4D6BQ4X.js.map +1 -0
  31. package/dist/{chunk-CACKC6XG.js → chunk-PGEDE6IM.js} +136 -89
  32. package/dist/chunk-PGEDE6IM.js.map +1 -0
  33. package/dist/{chunk-A4IBBWGO.js → chunk-RALHHPTU.js} +1 -1
  34. package/dist/chunk-RIDSPLE5.js +42 -0
  35. package/dist/chunk-RIDSPLE5.js.map +1 -0
  36. package/dist/{chunk-Z6VOBGTU.js → chunk-UOMHWUHV.js} +2 -12
  37. package/dist/chunk-UOMHWUHV.js.map +1 -0
  38. package/dist/{chunk-QREWE3NR.js → chunk-YONQYTVH.js} +2 -2
  39. package/dist/chunk-ZAN22NGL.js +13 -0
  40. package/dist/chunk-ZAN22NGL.js.map +1 -0
  41. package/dist/config/index.d.ts +200 -0
  42. package/dist/config/index.js +23 -0
  43. package/dist/config/index.js.map +1 -0
  44. package/dist/connector/index.d.ts +23 -5
  45. package/dist/connector/index.js +4 -1
  46. package/dist/core/index.d.ts +2 -2
  47. package/dist/core/index.js +1 -0
  48. package/dist/error/index.js +1 -0
  49. package/dist/generator/index.js +2 -0
  50. package/dist/generator/index.js.map +1 -1
  51. package/dist/index.d.ts +19 -16
  52. package/dist/index.js +68 -36
  53. package/dist/index.native.d.ts +18 -14
  54. package/dist/index.native.js +73 -34
  55. package/dist/index.web.d.ts +17 -14
  56. package/dist/index.web.js +68 -36
  57. package/dist/maintenance/index.d.ts +2 -2
  58. package/dist/maintenance/index.js +3 -2
  59. package/dist/platform/index.d.ts +1 -1
  60. package/dist/platform/index.js +2 -0
  61. package/dist/platform/index.js.map +1 -1
  62. package/dist/platform/index.native.d.ts +1 -1
  63. package/dist/platform/index.native.js +1 -0
  64. package/dist/platform/index.web.d.ts +1 -1
  65. package/dist/platform/index.web.js +1 -0
  66. package/dist/pol-attachment-queue-DqBvLAEY.d.ts +255 -0
  67. package/dist/provider/index.d.ts +149 -114
  68. package/dist/provider/index.js +9 -14
  69. package/dist/provider/index.native.d.ts +108 -0
  70. package/dist/provider/index.native.js +121 -0
  71. package/dist/provider/index.native.js.map +1 -0
  72. package/dist/provider/index.web.d.ts +16 -0
  73. package/dist/provider/index.web.js +112 -0
  74. package/dist/provider/index.web.js.map +1 -0
  75. package/dist/react/index.d.ts +16 -65
  76. package/dist/react/index.js +2 -9
  77. package/dist/storage/index.d.ts +5 -4
  78. package/dist/storage/index.js +12 -9
  79. package/dist/storage/index.native.d.ts +5 -4
  80. package/dist/storage/index.native.js +8 -5
  81. package/dist/storage/index.web.d.ts +5 -4
  82. package/dist/storage/index.web.js +11 -8
  83. package/dist/storage/upload/index.d.ts +4 -3
  84. package/dist/storage/upload/index.js +4 -2
  85. package/dist/storage/upload/index.native.d.ts +4 -3
  86. package/dist/storage/upload/index.native.js +4 -2
  87. package/dist/storage/upload/index.web.d.ts +2 -1
  88. package/dist/storage/upload/index.web.js +4 -2
  89. package/dist/{supabase-connector-C4YpH_l3.d.ts → supabase-connector-HMxBA9Kg.d.ts} +2 -2
  90. package/dist/sync/index.d.ts +155 -20
  91. package/dist/sync/index.js +13 -3
  92. package/dist/{types-CyvBaAl8.d.ts → types-6QHGELuY.d.ts} +4 -1
  93. package/dist/{types-Dv1uf0LZ.d.ts → types-B9MptP7E.d.ts} +7 -10
  94. package/dist/types-BhAEsJj-.d.ts +330 -0
  95. package/dist/{types-D0WcHrq6.d.ts → types-CGMibJKD.d.ts} +8 -0
  96. package/dist/{types-CpM2_LhU.d.ts → types-DqJnP50o.d.ts} +6 -1
  97. package/dist/{pol-attachment-queue-BE2HU3Us.d.ts → types-JCEhw2Lf.d.ts} +139 -346
  98. package/package.json +18 -4
  99. package/dist/chunk-654ERHA7.js +0 -1
  100. package/dist/chunk-BC2SRII2.js.map +0 -1
  101. package/dist/chunk-C5ODS3XH.js.map +0 -1
  102. package/dist/chunk-CAB26E6F.js.map +0 -1
  103. package/dist/chunk-CACKC6XG.js.map +0 -1
  104. package/dist/chunk-FNYQFILT.js +0 -44
  105. package/dist/chunk-FNYQFILT.js.map +0 -1
  106. package/dist/chunk-JCGOZVWL.js.map +0 -1
  107. package/dist/chunk-P4HZA6ZT.js.map +0 -1
  108. package/dist/chunk-RBPWEOIV.js +0 -358
  109. package/dist/chunk-RBPWEOIV.js.map +0 -1
  110. package/dist/chunk-RE5HWLCB.js.map +0 -1
  111. package/dist/chunk-XAEII4ZX.js.map +0 -1
  112. package/dist/chunk-Z6VOBGTU.js.map +0 -1
  113. /package/dist/{chunk-XOY2CJ67.js.map → chunk-4F5B5CZ7.js.map} +0 -0
  114. /package/dist/{chunk-654ERHA7.js.map → chunk-5WRI5ZAA.js.map} +0 -0
  115. /package/dist/{chunk-A4IBBWGO.js.map → chunk-RALHHPTU.js.map} +0 -0
  116. /package/dist/{chunk-QREWE3NR.js.map → chunk-YONQYTVH.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/storage/auth-error-utils.ts","../src/storage/SupabaseStorageAdapter.ts"],"sourcesContent":["/**\n * Utilities for detecting storage authentication errors.\n *\n * These utilities help identify when a storage operation failed due to\n * authentication issues (expired tokens, invalid sessions, etc.) that\n * can often be resolved by refreshing the user's session.\n *\n * NOTE: This is a copy of the utility from @pol-studios/db/storage.\n * We maintain a separate copy here to avoid circular dependencies between\n * @pol-studios/powersync and @pol-studios/db.\n */\n\n/**\n * Patterns that indicate an error is authentication-related.\n * These are checked against error messages when the status code alone\n * isn't sufficient to determine if it's an auth error.\n */\nconst AUTH_ERROR_PATTERNS = [/\\btoken\\b/i, /\\bauthoriz/i, /\\bauthenticat/i, /\\bsession\\b/i, /\\bexpired?\\b/i, /\\binvalid.*credential/i, /\\bno.*session\\b/i, /\\bJWT\\b/i] as const;\n\n/**\n * Check if a storage error is an authentication error.\n *\n * These errors can often be fixed by refreshing the session.\n *\n * @param error - The error to check (typically from Supabase Storage operations)\n * @returns true if the error appears to be authentication-related\n *\n * @example\n * ```typescript\n * const { data, error } = await storage.createSignedUrl(path, 3600);\n * if (error && isStorageAuthError(error)) {\n * await supabase.auth.refreshSession();\n * // Retry the request...\n * }\n * ```\n */\nexport function isStorageAuthError(error: unknown): boolean {\n if (!error) return false;\n const errorObj = error as {\n status?: number;\n statusCode?: number;\n message?: string;\n };\n const status = errorObj.status ?? errorObj.statusCode;\n\n // 401 (Unauthorized) and 403 (Forbidden) are always auth errors\n if (status === 401 || status === 403) {\n return true;\n }\n\n // 400 (Bad Request) is only an auth error when the message contains auth-related keywords\n // This is because custom gateways may return 400 with messages like\n // \"querystring must have required property 'token'\" for auth failures,\n // but 400 can also mean malformed parameters unrelated to auth\n if (status === 400) {\n const msg = errorObj.message ?? String(error);\n return /\\btoken\\b/i.test(msg) || /\\bauthoriz/i.test(msg) || /\\bauthenticat/i.test(msg);\n }\n\n // For other status codes (or no status), check the error message for auth-related keywords\n const message = errorObj.message ?? String(error);\n return AUTH_ERROR_PATTERNS.some(pattern => pattern.test(message));\n}\n\n/**\n * Extract a meaningful error message from a storage error.\n *\n * Supabase Storage errors may have different structures:\n * - Standard Error with `.message` property\n * - StorageError with `.message` and optional `.error` property\n * - Response-like object with `status`, `statusText`, `url` properties\n * - Plain string\n *\n * This function handles all these cases and returns a human-readable message.\n *\n * @param error - The error to extract a message from\n * @returns A human-readable error message\n *\n * @example\n * ```typescript\n * const { data, error } = await storage.createSignedUrl(path, 3600);\n * if (error) {\n * console.error(getStorageErrorMessage(error));\n * // \"Invalid path format\" or \"Object not found\" etc.\n * }\n * ```\n */\nexport function getStorageErrorMessage(error: unknown): string {\n if (!error) return 'Unknown error';\n\n // Handle plain strings\n if (typeof error === 'string') {\n return error;\n }\n\n // Handle Error objects and Supabase StorageError\n const errorObj = error as {\n message?: string;\n error?: string;\n status?: number;\n statusCode?: number;\n statusText?: string;\n name?: string;\n cause?: unknown;\n };\n\n // Prefer .message if it exists and is a non-empty string\n if (typeof errorObj.message === 'string' && errorObj.message.length > 0) {\n return errorObj.message;\n }\n\n // Check for .error property (some Supabase errors use this)\n if (typeof errorObj.error === 'string' && errorObj.error.length > 0) {\n return errorObj.error;\n }\n\n // Construct message from status code if available\n const status = errorObj.status ?? errorObj.statusCode;\n if (status) {\n const statusText = errorObj.statusText || getStatusText(status);\n return `HTTP ${status}: ${statusText}`;\n }\n\n // Fall back to JSON stringification, but handle circular references\n try {\n return JSON.stringify(error);\n } catch {\n return String(error);\n }\n}\n\n/**\n * Get a human-readable status text for common HTTP status codes.\n */\nfunction getStatusText(status: number): string {\n switch (status) {\n case 400:\n return 'Bad Request';\n case 401:\n return 'Unauthorized';\n case 403:\n return 'Forbidden';\n case 404:\n return 'Not Found';\n case 409:\n return 'Conflict';\n case 413:\n return 'Payload Too Large';\n case 422:\n return 'Unprocessable Entity';\n case 429:\n return 'Too Many Requests';\n case 500:\n return 'Internal Server Error';\n case 502:\n return 'Bad Gateway';\n case 503:\n return 'Service Unavailable';\n case 504:\n return 'Gateway Timeout';\n default:\n return 'Error';\n }\n}\n\n/**\n * Normalize a storage path by removing leading slashes.\n *\n * Supabase Storage expects paths without leading slashes.\n * This function ensures the path is properly formatted.\n *\n * @param path - The storage path to normalize\n * @returns The normalized path without leading slashes\n *\n * @example\n * ```typescript\n * normalizeStoragePath('/foo/bar.jpg'); // 'foo/bar.jpg'\n * normalizeStoragePath('foo/bar.jpg'); // 'foo/bar.jpg'\n * normalizeStoragePath('//foo/bar.jpg'); // 'foo/bar.jpg'\n * ```\n */\nexport function normalizeStoragePath(path: string): string {\n // Remove leading slashes\n let normalized = path;\n while (normalized.startsWith('/')) {\n normalized = normalized.slice(1);\n }\n return normalized;\n}","/**\n * Supabase Storage Adapter for @pol-studios/powersync\n *\n * Implements the unified SupabaseStorage interface for attachment operations.\n * Uses Supabase Storage with platform-agnostic file system operations.\n *\n * Key features:\n * - Memory-efficient downloads using signed URLs + native file system\n * - Multi-bucket support via BucketConfig/BucketResolver\n * - Platform-agnostic via FileSystemAdapter interface\n * - Implements unified SupabaseStorage interface\n */\n\nimport type { FileSystemAdapter, LoggerAdapter } from '../platform/types';\nimport type { SupabaseStorage, SupabaseClient } from './types';\nimport { resolveBucket } from './types';\nimport { isStorageAuthError, getStorageErrorMessage, normalizeStoragePath } from './auth-error-utils';\nimport { AbortError } from '../utils/retry';\n\n/**\n * Supabase Storage adapter implementing the unified SupabaseStorage interface.\n *\n * Uses PlatformAdapter's fileSystem for I/O operations to remain platform-agnostic.\n *\n * IMPORTANT: This adapter downloads files using signed URLs + platform file system\n * operations to avoid loading entire files into memory. This prevents OOM crashes\n * on large files that occurred with the previous blob-based approach.\n *\n * @example\n * ```typescript\n * const adapter = new SupabaseStorageAdapter(\n * {\n * client: supabaseClient,\n * defaultBucket: 'attachments',\n * bucketMap: new Map([['avatars/', 'user-avatars']]),\n * },\n * platform.fileSystem\n * );\n *\n * // Download returns file:// URI\n * const localUri = await adapter.download('photos/image.jpg');\n *\n * // Upload from local file\n * await adapter.upload('photos/new.jpg', localUri, 'image/jpeg');\n *\n * // Delete from remote storage\n * await adapter.delete('photos/old.jpg');\n * ```\n */\n/**\n * Image transform options for Supabase Storage.\n * Used to compress/resize images on download via Supabase's transform feature.\n */\nexport interface ImageTransformOptions {\n /** Enable image transformation (default: true) */\n enabled?: boolean;\n /** Image width in pixels (default: 2048) */\n width?: number;\n /** Image height in pixels (optional, maintains aspect ratio if omitted) */\n height?: number;\n /** Compression quality 1-100 (default: 70) */\n quality?: number;\n /** Output format (default: 'origin' to keep original format) */\n format?: 'origin' | 'avif' | 'webp';\n /** Resize mode (default: 'contain') */\n resize?: 'cover' | 'contain' | 'fill';\n}\n\n/**\n * Options for SupabaseStorageAdapter constructor.\n */\nexport interface SupabaseStorageAdapterOptions {\n client: SupabaseClient;\n defaultBucket: string;\n bucketMap?: Map<string, string>;\n bucketResolver?: (storagePath: string) => string | undefined;\n signedUrlExpiry?: number;\n logger?: LoggerAdapter;\n /** Image transform options for compression on download */\n imageTransform?: ImageTransformOptions;\n}\n\n// Image extensions that support Supabase transform\nconst IMAGE_EXTENSIONS = new Set(['.jpg', '.jpeg', '.png', '.webp', '.avif', '.gif', '.heic', '.heif']);\nexport class SupabaseStorageAdapter implements SupabaseStorage {\n private client: SupabaseClient;\n private defaultBucket: string;\n private bucketMap?: Map<string, string>;\n private bucketResolver?: (storagePath: string) => string | undefined;\n private signedUrlExpiry: number;\n private fileSystem?: FileSystemAdapter;\n private logger?: LoggerAdapter;\n private imageTransform?: ImageTransformOptions;\n constructor(options: SupabaseStorageAdapterOptions, fileSystem?: FileSystemAdapter) {\n this.client = options.client;\n this.defaultBucket = options.defaultBucket;\n this.bucketMap = options.bucketMap;\n this.bucketResolver = options.bucketResolver;\n this.signedUrlExpiry = options.signedUrlExpiry ?? 60;\n this.fileSystem = fileSystem;\n this.logger = options.logger;\n this.imageTransform = options.imageTransform;\n }\n\n /**\n * Update image transform options (can be set after construction).\n */\n setImageTransform(options: ImageTransformOptions): void {\n this.imageTransform = options;\n }\n\n /**\n * Check if a file path is an image that supports transformation.\n */\n private isTransformableImage(path: string): boolean {\n const ext = path.slice(path.lastIndexOf('.')).toLowerCase();\n return IMAGE_EXTENSIONS.has(ext);\n }\n\n /**\n * Set the file system adapter (can be set after construction).\n */\n setFileSystem(fileSystem: FileSystemAdapter): void {\n this.fileSystem = fileSystem;\n }\n\n /**\n * Resolve the storage bucket for a given file path.\n */\n resolveBucket(filePath: string): string {\n return resolveBucket({\n defaultBucket: this.defaultBucket,\n bucketMap: this.bucketMap,\n bucketResolver: this.bucketResolver\n }, filePath);\n }\n\n // ─── SupabaseStorage Interface ──────────────────────────────────────────────\n\n /**\n * Download a file from Supabase Storage and return as a file:// URI.\n *\n * Uses streaming download to a temp file to avoid loading the entire file\n * into memory. This prevents OOM crashes and FileReader hangs that occur\n * with blob-based downloads in React Native after ~115 downloads.\n *\n * Implements SupabaseStorage.download\n *\n * @param path - Remote storage path\n * @returns file:// URI pointing to the downloaded temp file\n */\n async download(path: string): Promise<string> {\n if (!this.fileSystem) {\n throw new Error('FileSystem adapter not configured - cannot download file');\n }\n\n // Use streaming download to temp file (memory-efficient)\n const cacheDir = this.fileSystem.getCacheDirectory();\n const tempFileName = `download_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;\n const tempFilePath = `${cacheDir}${tempFileName}`;\n await this.downloadToPath(path, tempFilePath);\n\n // Return file:// URI for efficient file copy path\n return tempFilePath.startsWith('file://') ? tempFilePath : `file://${tempFilePath}`;\n }\n\n /**\n * Download a file from Supabase Storage to a specific local path.\n *\n * Uses the platform's streaming downloadFile method to avoid loading\n * the entire file into memory (prevents OOM on large files).\n *\n * @param remotePath - Remote storage path\n * @param localPath - Local file path to write to\n */\n async downloadToPath(remotePath: string, localPath: string): Promise<void> {\n if (!this.fileSystem) {\n throw new Error('FileSystem adapter not configured');\n }\n const signedUrl = await this.getDownloadUrl(remotePath);\n\n // Use the platform's streaming download method\n // This writes directly to disk without loading the entire file into memory\n await this.fileSystem.downloadFile(signedUrl, localPath);\n }\n\n /**\n * Upload a local file to Supabase Storage.\n *\n * Implements SupabaseStorage.upload\n *\n * @param path - Remote storage path\n * @param localUri - Local file:// URI to upload from\n * @param mediaType - MIME type of the file\n * @param signal - Optional AbortSignal for cancellation\n */\n async upload(path: string, localUri: string, mediaType: string, signal?: AbortSignal): Promise<void> {\n // Check if already aborted\n if (signal?.aborted) {\n throw new AbortError('Upload aborted');\n }\n if (!this.fileSystem) {\n throw new Error('FileSystem adapter not configured - cannot upload file');\n }\n const normalizedPath = normalizeStoragePath(path);\n const bucket = this.resolveBucket(normalizedPath);\n this.logger?.debug?.(`[SupabaseStorageAdapter] Uploading file`, {\n bucket,\n path: normalizedPath,\n localUri,\n mediaType\n });\n\n // Read file content as base64 and convert to blob\n const base64Content = await this.fileSystem.readFile(localUri, 'base64');\n\n // Check abort after file read\n if (signal?.aborted) {\n throw new AbortError('Upload aborted');\n }\n const binaryString = atob(base64Content);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n const fileData = new Blob([bytes], {\n type: mediaType\n });\n\n // Upload to Supabase\n const {\n error\n } = await this.client.storage.from(bucket).upload(normalizedPath, fileData, {\n contentType: mediaType,\n upsert: true\n });\n if (error) {\n const errorMessage = getStorageErrorMessage(error);\n throw new Error(`Upload failed for '${normalizedPath}' in bucket '${bucket}': ${errorMessage}`);\n }\n this.logger?.info?.(`[SupabaseStorageAdapter] Upload completed`, {\n path: normalizedPath,\n bucket\n });\n }\n\n /**\n * Delete a file from Supabase Storage.\n *\n * Implements SupabaseStorage.delete\n *\n * @param path - Remote storage path to delete\n */\n async delete(path: string): Promise<void> {\n const normalizedPath = normalizeStoragePath(path);\n const bucket = this.resolveBucket(normalizedPath);\n this.logger?.debug?.(`[SupabaseStorageAdapter] Deleting file`, {\n bucket,\n path: normalizedPath\n });\n const {\n error\n } = await this.client.storage.from(bucket).remove([normalizedPath]);\n if (error) {\n const errorMessage = getStorageErrorMessage(error);\n throw new Error(`Delete failed for '${normalizedPath}' in bucket '${bucket}': ${errorMessage}`);\n }\n this.logger?.info?.(`[SupabaseStorageAdapter] Delete completed`, {\n path: normalizedPath,\n bucket\n });\n }\n\n /**\n * Get a signed download URL for a remote file.\n *\n * Includes session refresh logic to handle stale tokens during background downloads.\n * If the initial request fails with an auth error (400/401/403), refreshes the session\n * and retries once.\n */\n async getDownloadUrl(remotePath: string): Promise<string> {\n // Normalize the path to remove any leading slashes\n // Supabase Storage expects paths without leading slashes\n const normalizedPath = normalizeStoragePath(remotePath);\n const bucket = this.resolveBucket(normalizedPath);\n\n // Build transform options for images if enabled\n const useTransform = this.imageTransform?.enabled !== false && this.isTransformableImage(normalizedPath);\n const signedUrlOptions = useTransform && this.imageTransform ? {\n transform: {\n width: this.imageTransform.width ?? 2048,\n height: this.imageTransform.height,\n quality: this.imageTransform.quality ?? 70,\n format: this.imageTransform.format ?? 'origin' as const,\n resize: this.imageTransform.resize ?? 'contain' as const\n }\n } : undefined;\n this.logger?.debug?.(`[SupabaseStorageAdapter] Creating signed URL`, {\n bucket,\n path: normalizedPath,\n originalPath: remotePath !== normalizedPath ? remotePath : undefined,\n transform: useTransform ? signedUrlOptions?.transform : undefined\n });\n\n // First attempt\n const {\n data,\n error\n } = await this.client.storage.from(bucket).createSignedUrl(normalizedPath, this.signedUrlExpiry, signedUrlOptions);\n if (error) {\n const errorMessage = getStorageErrorMessage(error);\n\n // Check if this is an auth error that might be fixed by refreshing the session\n const isAuthError = isStorageAuthError(error);\n if (isAuthError) {\n this.logger?.warn?.(`[SupabaseStorageAdapter] Auth error creating signed URL, refreshing session and retrying:`, {\n path: normalizedPath,\n bucket,\n error: errorMessage\n });\n try {\n // Refresh the session\n await this.client.auth.refreshSession();\n\n // Retry the request with same transform options\n const {\n data: retryData,\n error: retryError\n } = await this.client.storage.from(bucket).createSignedUrl(normalizedPath, this.signedUrlExpiry, signedUrlOptions);\n if (retryError) {\n const retryErrorMessage = getStorageErrorMessage(retryError);\n throw new Error(`Failed to create signed URL after session refresh: ${retryErrorMessage}`);\n }\n if (!retryData?.signedUrl) {\n throw new Error('Failed to create signed URL after session refresh: No URL returned');\n }\n this.logger?.info?.(`[SupabaseStorageAdapter] Successfully created signed URL after session refresh`);\n return retryData.signedUrl;\n } catch (refreshError) {\n // If refresh also fails, throw the original error with context\n throw new Error(`Failed to create signed URL: ${errorMessage} (session refresh also failed: ${refreshError instanceof Error ? refreshError.message : String(refreshError)})`);\n }\n }\n\n // Non-auth error, throw with normalized error message\n throw new Error(`Failed to create signed URL for '${normalizedPath}' in bucket '${bucket}': ${errorMessage}`);\n }\n if (!data?.signedUrl) {\n throw new Error(`Failed to create signed URL for '${normalizedPath}' in bucket '${bucket}': No URL returned`);\n }\n return data.signedUrl;\n }\n\n /**\n * Check if a file exists in remote storage.\n */\n async exists(remotePath: string): Promise<boolean> {\n const bucket = this.resolveBucket(remotePath);\n try {\n // Use list with a specific path prefix to check existence\n const pathParts = remotePath.split('/');\n const fileName = pathParts.pop();\n const directory = pathParts.join('/');\n const {\n data,\n error\n } = await this.client.storage.from(bucket).list(directory, {\n search: fileName,\n limit: 1\n });\n if (error) {\n return false;\n }\n return data?.some((file: {\n name: string;\n }) => file.name === fileName) ?? false;\n } catch {\n return false;\n }\n }\n\n // ─── Backward Compatibility (AttachmentStorageAdapter) ─────────────────────\n\n /**\n * Download a file from remote storage.\n * Alias for download() - for backward compatibility with AttachmentStorageAdapter interface.\n *\n * @param filePath - Remote storage path\n * @returns file:// URI pointing to the downloaded temp file\n */\n async downloadFile(filePath: string): Promise<string> {\n return this.download(filePath);\n }\n\n // ─── Local File Operations ─────────────────────────────────────────────────\n\n /**\n * Write data to a local file.\n */\n async writeFile(fileURI: string, data: string, options?: {\n encoding?: 'utf8' | 'base64';\n }): Promise<void> {\n if (!this.fileSystem) {\n throw new Error('FileSystem adapter not configured');\n }\n const encoding = options?.encoding ?? 'utf8';\n\n // Ensure parent directory exists\n const parentDir = fileURI.substring(0, fileURI.lastIndexOf('/'));\n if (parentDir) {\n await this.fileSystem.makeDirectory(parentDir, {\n intermediates: true\n });\n }\n await this.fileSystem.writeFile(fileURI, data, encoding);\n }\n\n /**\n * Read data from a local file.\n */\n async readFile(fileURI: string, options?: {\n encoding?: 'utf8' | 'base64';\n mediaType?: string;\n }): Promise<ArrayBuffer> {\n if (!this.fileSystem) {\n throw new Error('FileSystem adapter not configured');\n }\n const encoding = options?.encoding ?? 'utf8';\n const content = await this.fileSystem.readFile(fileURI, encoding);\n if (encoding === 'base64') {\n // Decode base64 to ArrayBuffer\n const binaryString = atob(content);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n return bytes.buffer;\n }\n\n // For utf8, encode to ArrayBuffer\n const encoder = new TextEncoder();\n return encoder.encode(content).buffer;\n }\n\n /**\n * Delete a local file.\n */\n async deleteFile(uri: string, _options?: {\n filename?: string;\n }): Promise<void> {\n if (!this.fileSystem) {\n throw new Error('FileSystem adapter not configured');\n }\n await this.fileSystem.deleteFile(uri);\n }\n\n /**\n * Check if a local file exists.\n */\n async fileExists(fileURI: string): Promise<boolean> {\n if (!this.fileSystem) {\n throw new Error('FileSystem adapter not configured');\n }\n const info = await this.fileSystem.getFileInfo(fileURI);\n return info?.exists ?? false;\n }\n\n /**\n * Create a directory.\n */\n async makeDir(uri: string): Promise<void> {\n if (!this.fileSystem) {\n throw new Error('FileSystem adapter not configured');\n }\n await this.fileSystem.makeDirectory(uri, {\n intermediates: true\n });\n }\n\n /**\n * Copy a file.\n */\n async copyFile(sourceUri: string, targetUri: string): Promise<void> {\n if (!this.fileSystem) {\n throw new Error('FileSystem adapter not configured');\n }\n await this.fileSystem.copyFile(sourceUri, targetUri);\n }\n\n /**\n * Get the user storage directory.\n */\n getUserStorageDirectory(): string {\n if (!this.fileSystem) {\n throw new Error('FileSystem adapter not configured');\n }\n return this.fileSystem.getDocumentsDirectory();\n }\n}\n\n/**\n * Create a SupabaseStorageAdapter with simplified configuration.\n *\n * @example\n * ```typescript\n * const adapter = createSupabaseStorageAdapter(\n * supabaseClient,\n * 'attachments',\n * platform.fileSystem\n * );\n * ```\n */\nexport function createSupabaseStorageAdapter(client: SupabaseClient, defaultBucket: string, fileSystem?: FileSystemAdapter, options?: {\n bucketMap?: Map<string, string>;\n bucketResolver?: (storagePath: string) => string | undefined;\n signedUrlExpiry?: number;\n logger?: LoggerAdapter;\n}): SupabaseStorageAdapter {\n return new SupabaseStorageAdapter({\n client,\n defaultBucket,\n bucketMap: options?.bucketMap,\n bucketResolver: options?.bucketResolver,\n signedUrlExpiry: options?.signedUrlExpiry,\n logger: options?.logger\n }, fileSystem);\n}"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/storage/types.ts","../src/storage/upload/types.ts"],"sourcesContent":["/**\n * Unified Storage Types for @pol-studios/powersync\n *\n * Single storage interface for all Supabase storage operations.\n * Consolidates the previous 6 fragmented interfaces into one:\n * - AttachmentStorageAdapter (attachments/types.ts)\n * - RemoteStorageAdapter (storage/types.ts - removed)\n * - StorageAdapter (@powersync/attachments)\n * - PowerSyncStorageAdapter (storage/types.ts - removed)\n * - StorageUploadHandler (storage/types.ts - removed)\n * - UploadHandler (attachments/types.ts)\n */\n\n// ─── Unified Storage Interface ──────────────────────────────────────────────\n\n/**\n * Unified storage interface for Supabase storage operations.\n * Handles both upload and download for attachments.\n *\n * This is the ONLY storage interface consumers need to implement.\n * It replaces all previous storage adapter interfaces.\n *\n * @example\n * ```typescript\n * const storage: SupabaseStorage = {\n * async download(path) {\n * const { data } = await supabase.storage\n * .from('attachments')\n * .createSignedUrl(path, 60);\n * // Download to temp file and return URI\n * return tempFileUri;\n * },\n *\n * async upload(path, localUri, mediaType) {\n * const file = await readFile(localUri);\n * await supabase.storage\n * .from('attachments')\n * .upload(path, file, { contentType: mediaType });\n * },\n * };\n * ```\n */\nexport interface SupabaseStorage {\n /**\n * Download a file and return the local file:// URI.\n *\n * @param path - Remote storage path\n * @returns Local file:// URI pointing to the downloaded file\n */\n download(path: string): Promise<string>;\n\n /**\n * Upload a local file to remote storage.\n *\n * @param path - Remote storage path\n * @param localUri - Local file:// URI to upload from\n * @param mediaType - MIME type of the file\n * @param signal - Optional AbortSignal for cancellation\n */\n upload(path: string, localUri: string, mediaType: string, signal?: AbortSignal): Promise<void>;\n\n /**\n * Delete a file from remote storage.\n * Optional - not all implementations need deletion.\n *\n * @param path - Remote storage path to delete\n */\n delete?(path: string): Promise<void>;\n\n /**\n * Resolve which bucket a path belongs to.\n * Optional - useful for multi-bucket configurations.\n *\n * @param path - Remote storage path\n * @returns The bucket name\n */\n resolveBucket?(path: string): string;\n}\n\n// ─── Storage Backend (Factory API) ──────────────────────────────────────────\n\n/**\n * Download result discriminated union.\n * Native platforms return file URIs, web returns blobs.\n */\nexport type DownloadResult = {\n type: 'file';\n uri: string;\n} | {\n type: 'blob';\n data: Blob;\n};\n\n/**\n * Storage backend returned by createSupabaseStorage factory.\n * This is a more flexible interface that returns discriminated unions\n * to handle both native (file URI) and web (blob) download results.\n *\n * For simpler use cases, use SupabaseStorage interface directly.\n */\nexport interface StorageBackend {\n /** Download a file, returns discriminated union based on platform */\n download(path: string): Promise<DownloadResult>;\n\n /** Upload a local file to remote storage */\n upload(path: string, localUri: string, contentType: string, options?: StorageUploadOptions): Promise<void>;\n\n /** Delete a file from remote storage */\n delete(path: string): Promise<void>;\n\n /** Resolve which bucket a path belongs to */\n resolveBucket(path: string): string;\n}\n\n// ─── Upload Progress ────────────────────────────────────────────────────────\n\n/**\n * Progress information for upload operations.\n */\nexport interface UploadProgress {\n /** Bytes uploaded so far */\n loaded: number;\n /** Total bytes to upload */\n total: number;\n}\n\n// ─── Upload Options ─────────────────────────────────────────────────────────\n\n/**\n * Options for upload operations.\n */\nexport interface StorageUploadOptions {\n /** AbortSignal for cancellation support */\n signal?: AbortSignal;\n /** Progress callback */\n onProgress?: (progress: UploadProgress) => void;\n}\n\n// ─── Supabase Storage Options ───────────────────────────────────────────────\n\nimport type { LoggerAdapter, FileSystemAdapter } from '../platform/types';\n\n/**\n * Options for creating a Supabase storage backend.\n */\nexport interface SupabaseStorageOptions {\n /** Supabase client instance */\n client: SupabaseClient;\n\n /** Default storage bucket */\n defaultBucket: string;\n\n /**\n * Map of path prefixes to bucket names.\n * Checked in iteration order (use Map to preserve order).\n *\n * @example\n * ```typescript\n * bucketMap: new Map([\n * ['avatars/', 'user-avatars'],\n * ['docs/', 'documents'],\n * ])\n * ```\n */\n bucketMap?: Map<string, string>;\n\n /**\n * Custom bucket resolver function.\n * Takes precedence over bucketMap.\n * Return undefined to fall through to bucketMap/defaultBucket.\n */\n bucketResolver?: (storagePath: string) => string | undefined;\n\n /**\n * Signed URL expiry in seconds.\n * @default 60 (short-lived for security)\n */\n signedUrlExpiry?: number;\n\n /**\n * File system adapter for native file operations.\n * Required for native platforms to enable file-based downloads.\n */\n fileSystem?: FileSystemAdapter;\n\n /**\n * Optional logger for diagnostic messages.\n */\n logger?: LoggerAdapter;\n}\n\n// ─── Supabase Client Type ───────────────────────────────────────────────────\n\n/**\n * Transform options for Supabase image transformation.\n * See: https://supabase.com/docs/guides/storage/serving/image-transformations\n */\nexport interface SupabaseTransformOptions {\n width?: number;\n height?: number;\n resize?: 'cover' | 'contain' | 'fill';\n format?: 'origin' | 'avif' | 'webp';\n quality?: number; // 1-100\n}\n\n/**\n * Minimal Supabase client interface for storage operations.\n * Using a minimal interface to avoid version conflicts with @supabase/supabase-js.\n */\nexport interface SupabaseClient {\n storage: {\n from(bucket: string): {\n createSignedUrl(path: string, expiresIn: number, options?: {\n download?: string | boolean;\n transform?: SupabaseTransformOptions;\n }): Promise<{\n data: {\n signedUrl: string;\n } | null;\n error: Error | null;\n }>;\n upload(path: string, file: Blob | ArrayBuffer | FormData, options?: {\n contentType?: string;\n upsert?: boolean;\n }): Promise<{\n data: {\n path: string;\n } | null;\n error: Error | null;\n }>;\n remove(paths: string[]): Promise<{\n data: {\n name: string;\n }[] | null;\n error: Error | null;\n }>;\n list(path?: string, options?: {\n search?: string;\n limit?: number;\n }): Promise<{\n data: {\n name: string;\n }[] | null;\n error: Error | null;\n }>;\n };\n };\n auth: {\n refreshSession(): Promise<{\n data: unknown;\n error: Error | null;\n }>;\n };\n}\n\n// ─── Bucket Resolution Helpers ──────────────────────────────────────────────\n\n/**\n * Resolve bucket from storage path using SupabaseStorageOptions.\n *\n * Resolution order:\n * 1. Custom bucketResolver (if provided and returns a value)\n * 2. bucketMap prefix matching (if provided)\n * 3. defaultBucket\n */\nexport function resolveBucket(options: Pick<SupabaseStorageOptions, 'defaultBucket' | 'bucketMap' | 'bucketResolver'>, storagePath: string): string {\n // Try custom resolver first\n if (options.bucketResolver) {\n const resolved = options.bucketResolver(storagePath);\n if (resolved !== undefined) {\n return resolved;\n }\n }\n\n // Try bucket map prefix matching\n if (options.bucketMap) {\n for (const [prefix, bucket] of options.bucketMap) {\n if (storagePath.startsWith(prefix)) {\n return bucket;\n }\n }\n }\n\n // Fall back to default\n return options.defaultBucket;\n}","/**\n * Upload Handler Types for @pol-studios/powersync\n *\n * Defines types for platform-specific upload handlers.\n */\n\n/**\n * Bucket configuration for multi-bucket routing.\n */\nexport interface BucketConfig {\n /** Default bucket for storage operations */\n defaultBucket: string;\n\n /** Map of path prefixes to bucket names */\n bucketMap?: Map<string, string>;\n\n /** Custom bucket resolver function */\n resolver?: (storagePath: string) => string | undefined;\n}\n\n/**\n * Options for creating a SupabaseUploadHandler.\n */\nexport interface SupabaseUploadHandlerOptions {\n /** Supabase client instance */\n supabaseClient: any; // SupabaseClient - using any to avoid version conflicts\n\n /** Bucket configuration for multi-bucket routing */\n bucketConfig: BucketConfig;\n}\n\n/**\n * Event handlers for upload progress tracking.\n */\nexport interface UploadEventHandlers {\n /** Called with progress updates during upload */\n onProgress?: (progress: {\n loaded: number;\n total: number;\n }) => void;\n\n /** Called when upload completes successfully */\n onComplete?: () => void;\n\n /** Called when upload fails */\n onError?: (error: Error) => void;\n}\n\n/**\n * Configuration for native upload notifications (Android).\n */\nexport interface UploadNotificationConfig {\n /** Whether to show upload notifications */\n enabled: boolean;\n\n /** Whether to auto-clear notification on completion */\n autoClear: boolean;\n\n /** Title shown during upload progress */\n onProgressTitle: string;\n\n /** Title shown when upload completes */\n onCompleteTitle: string;\n\n /** Title shown when upload fails */\n onErrorTitle: string;\n}\n\n/**\n * Default notification configuration for Android uploads.\n */\nexport const DEFAULT_UPLOAD_NOTIFICATION: UploadNotificationConfig = {\n enabled: true,\n autoClear: true,\n onProgressTitle: 'Uploading...',\n onCompleteTitle: 'Upload complete',\n onErrorTitle: 'Upload failed'\n};"],"mappings":";AAyQO,SAAS,cAAc,SAAyF,aAA6B;AAElJ,MAAI,QAAQ,gBAAgB;AAC1B,UAAM,WAAW,QAAQ,eAAe,WAAW;AACnD,QAAI,aAAa,QAAW;AAC1B,aAAO;AAAA,IACT;AAAA,EACF;AAGA,MAAI,QAAQ,WAAW;AACrB,eAAW,CAAC,QAAQ,MAAM,KAAK,QAAQ,WAAW;AAChD,UAAI,YAAY,WAAW,MAAM,GAAG;AAClC,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAGA,SAAO,QAAQ;AACjB;;;ACtNO,IAAM,8BAAwD;AAAA,EACnE,SAAS;AAAA,EACT,WAAW;AAAA,EACX,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,cAAc;AAChB;","names":[]}