@pol-studios/powersync 1.0.7 → 1.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +933 -0
- package/dist/CacheSettingsManager-uz-kbnRH.d.ts +461 -0
- package/dist/attachments/index.d.ts +709 -6
- package/dist/attachments/index.js +133 -5
- package/dist/chunk-24RDMMCL.js +44 -0
- package/dist/chunk-24RDMMCL.js.map +1 -0
- package/dist/chunk-4TXTAEF2.js +2060 -0
- package/dist/chunk-4TXTAEF2.js.map +1 -0
- package/dist/chunk-63PXSPIN.js +358 -0
- package/dist/chunk-63PXSPIN.js.map +1 -0
- package/dist/chunk-654ERHA7.js +1 -0
- package/dist/{chunk-BREGB4WL.js → chunk-BRXQNASY.js} +287 -335
- package/dist/chunk-BRXQNASY.js.map +1 -0
- package/dist/{chunk-DHYUBVP7.js → chunk-CAB26E6F.js} +20 -9
- package/dist/chunk-CAB26E6F.js.map +1 -0
- package/dist/{chunk-H772V6XQ.js → chunk-CUCAYK7Z.js} +7 -43
- package/dist/chunk-CUCAYK7Z.js.map +1 -0
- package/dist/{chunk-4C3RY5SU.js → chunk-HWSNV45P.js} +76 -1
- package/dist/chunk-HWSNV45P.js.map +1 -0
- package/dist/{chunk-HFOFLW5F.js → chunk-KN2IZERF.js} +139 -6
- package/dist/chunk-KN2IZERF.js.map +1 -0
- package/dist/{chunk-UEYRTLKE.js → chunk-P4HZA6ZT.js} +20 -9
- package/dist/chunk-P4HZA6ZT.js.map +1 -0
- package/dist/chunk-T4AO7JIG.js +1 -0
- package/dist/{chunk-XQAJM2MW.js → chunk-VACPAAQZ.js} +33 -2
- package/dist/{chunk-XQAJM2MW.js.map → chunk-VACPAAQZ.js.map} +1 -1
- package/dist/{chunk-53WH2JJV.js → chunk-WN5ZJ3E2.js} +5 -8
- package/dist/chunk-WN5ZJ3E2.js.map +1 -0
- package/dist/chunk-XAEII4ZX.js +456 -0
- package/dist/chunk-XAEII4ZX.js.map +1 -0
- package/dist/chunk-XOY2CJ67.js +289 -0
- package/dist/chunk-XOY2CJ67.js.map +1 -0
- package/dist/chunk-YHTZ7VMV.js +1 -0
- package/dist/{chunk-MKD2VCX3.js → chunk-Z6VOBGTU.js} +8 -8
- package/dist/chunk-Z6VOBGTU.js.map +1 -0
- package/dist/chunk-ZM4ENYMF.js +230 -0
- package/dist/chunk-ZM4ENYMF.js.map +1 -0
- package/dist/connector/index.d.ts +56 -3
- package/dist/connector/index.js +8 -5
- package/dist/core/index.d.ts +12 -1
- package/dist/core/index.js +3 -2
- package/dist/error/index.js +0 -1
- package/dist/index.d.ts +12 -10
- package/dist/index.js +191 -29
- package/dist/index.native.d.ts +11 -9
- package/dist/index.native.js +191 -29
- package/dist/index.web.d.ts +11 -9
- package/dist/index.web.js +191 -29
- package/dist/maintenance/index.js +0 -1
- package/dist/platform/index.js +0 -2
- package/dist/platform/index.js.map +1 -1
- package/dist/platform/index.native.js +1 -2
- package/dist/platform/index.web.js +0 -1
- package/dist/pol-attachment-queue-BVAIueoP.d.ts +817 -0
- package/dist/provider/index.d.ts +38 -34
- package/dist/provider/index.js +11 -12
- package/dist/react/index.d.ts +372 -0
- package/dist/react/index.js +25 -0
- package/dist/storage/index.d.ts +3 -3
- package/dist/storage/index.js +22 -8
- package/dist/storage/index.native.d.ts +3 -3
- package/dist/storage/index.native.js +21 -7
- package/dist/storage/index.web.d.ts +3 -3
- package/dist/storage/index.web.js +21 -7
- package/dist/storage/upload/index.d.ts +7 -8
- package/dist/storage/upload/index.js +3 -3
- package/dist/storage/upload/index.native.d.ts +7 -8
- package/dist/storage/upload/index.native.js +4 -3
- package/dist/storage/upload/index.web.d.ts +1 -4
- package/dist/storage/upload/index.web.js +3 -3
- package/dist/supabase-connector-T9vHq_3i.d.ts +202 -0
- package/dist/sync/index.js +3 -3
- package/dist/{supabase-connector-qLm-WHkM.d.ts → types-B212hgfA.d.ts} +48 -170
- package/dist/{types-BVacP54t.d.ts → types-CyvBaAl8.d.ts} +12 -4
- package/dist/types-D0WcHrq6.d.ts +234 -0
- package/package.json +18 -4
- package/dist/CacheSettingsManager-1exbOC6S.d.ts +0 -261
- package/dist/chunk-4C3RY5SU.js.map +0 -1
- package/dist/chunk-53WH2JJV.js.map +0 -1
- package/dist/chunk-BREGB4WL.js.map +0 -1
- package/dist/chunk-DGUM43GV.js +0 -11
- package/dist/chunk-DHYUBVP7.js.map +0 -1
- package/dist/chunk-GKF7TOMT.js +0 -1
- package/dist/chunk-H772V6XQ.js.map +0 -1
- package/dist/chunk-HFOFLW5F.js.map +0 -1
- package/dist/chunk-KGSFAE5B.js +0 -1
- package/dist/chunk-LNL64IJZ.js +0 -1
- package/dist/chunk-MKD2VCX3.js.map +0 -1
- package/dist/chunk-UEYRTLKE.js.map +0 -1
- package/dist/chunk-WQ5MPAVC.js +0 -449
- package/dist/chunk-WQ5MPAVC.js.map +0 -1
- package/dist/chunk-ZEOKPWUC.js +0 -1165
- package/dist/chunk-ZEOKPWUC.js.map +0 -1
- package/dist/pol-attachment-queue-C7YNXXhK.d.ts +0 -676
- package/dist/types-Bgvx7-E8.d.ts +0 -187
- /package/dist/{chunk-DGUM43GV.js.map → chunk-654ERHA7.js.map} +0 -0
- /package/dist/{chunk-GKF7TOMT.js.map → chunk-T4AO7JIG.js.map} +0 -0
- /package/dist/{chunk-KGSFAE5B.js.map → chunk-YHTZ7VMV.js.map} +0 -0
- /package/dist/{chunk-LNL64IJZ.js.map → react/index.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/attachments/pol-storage-adapter.ts","../src/attachments/types.ts","../src/attachments/pol-attachment-queue.ts"],"sourcesContent":["/**\n * POL Storage Adapter\n *\n * Implements the official @powersync/attachments StorageAdapter interface,\n * bridging our PlatformAdapter file system and remote storage adapters.\n *\n * This adapter combines:\n * - Local file operations via PlatformAdapter.fileSystem\n * - Remote upload/download via AttachmentStorageAdapter (e.g., Supabase)\n */\n\nimport { EncodingType, StorageAdapter } from '@powersync/attachments';\nimport type { PlatformAdapter } from '../platform/types';\nimport type { AttachmentStorageAdapter } from './types';\n\n// Re-export EncodingType for convenience\nexport { EncodingType };\nexport interface PolStorageAdapterOptions {\n /**\n * Platform adapter for local file system operations.\n */\n platform: PlatformAdapter;\n\n /**\n * Remote storage adapter for upload/download operations.\n */\n remoteStorage: AttachmentStorageAdapter;\n\n /**\n * Base directory name for attachments within the cache directory.\n * Defaults to 'attachments'.\n */\n attachmentDirectoryName?: string;\n}\n\n/**\n * Storage adapter that implements the official @powersync/attachments interface.\n *\n * Combines local file system operations (via PlatformAdapter) with remote\n * storage operations (via AttachmentStorageAdapter like Supabase).\n *\n * @example\n * ```typescript\n * const storageAdapter = new PolStorageAdapter({\n * platform: platformAdapter,\n * remoteStorage: supabaseStorageAdapter,\n * });\n * ```\n */\nexport class PolStorageAdapter implements StorageAdapter {\n private readonly platform: PlatformAdapter;\n private readonly remoteStorage: AttachmentStorageAdapter;\n private readonly attachmentDirectoryName: string;\n private readonly userStorageDirectory: string;\n constructor(options: PolStorageAdapterOptions) {\n this.platform = options.platform;\n this.remoteStorage = options.remoteStorage;\n this.attachmentDirectoryName = options.attachmentDirectoryName ?? 'attachments';\n\n // User storage directory is the cache directory + attachment directory name\n // Must end with a '/' per the interface contract\n const cacheDir = this.platform.fileSystem.getCacheDirectory();\n this.userStorageDirectory = cacheDir.endsWith('/') ? `${cacheDir}${this.attachmentDirectoryName}/` : `${cacheDir}/${this.attachmentDirectoryName}/`;\n }\n\n // ─── Remote Operations ──────────────────────────────────────────────────────\n\n /**\n * Upload a file to remote storage.\n */\n async uploadFile(filePath: string, data: ArrayBuffer, options?: {\n mediaType?: string;\n }): Promise<void> {\n if (!this.remoteStorage.uploadFile) {\n throw new Error('Remote storage adapter does not support uploads');\n }\n\n // Convert ArrayBuffer to base64 for our upload interface\n const base64 = this._arrayBufferToBase64(data);\n await this.remoteStorage.uploadFile(filePath, base64);\n }\n\n /**\n * Download a file from remote storage.\n */\n async downloadFile(filePath: string): Promise<Blob> {\n const result = await this.remoteStorage.downloadFile(filePath);\n\n // Our adapter returns Blob | string, convert string to Blob if needed\n if (typeof result === 'string') {\n // Assume base64 string\n const binaryString = atob(result);\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 new Blob([bytes]);\n }\n return result;\n }\n\n // ─── Local File Operations ──────────────────────────────────────────────────\n\n /**\n * Write data to a local file.\n */\n async writeFile(fileUri: string, base64Data: string, options?: {\n encoding?: EncodingType;\n }): Promise<void> {\n const encoding = options?.encoding === EncodingType.UTF8 ? 'utf8' : 'base64';\n await this.platform.fileSystem.writeFile(fileUri, base64Data, encoding);\n }\n\n /**\n * Read a local file's contents.\n */\n async readFile(fileUri: string, options?: {\n encoding?: EncodingType;\n mediaType?: string;\n }): Promise<ArrayBuffer> {\n const encoding = options?.encoding === EncodingType.UTF8 ? 'utf8' : 'base64';\n const content = await this.platform.fileSystem.readFile(fileUri, encoding);\n\n // Convert base64 or UTF8 string to ArrayBuffer\n if (encoding === 'base64') {\n return this._base64ToArrayBuffer(content);\n } else {\n // UTF8 string to ArrayBuffer\n const encoder = new TextEncoder();\n return encoder.encode(content).buffer;\n }\n }\n\n /**\n * Delete a local file.\n */\n async deleteFile(uri: string, _options?: {\n filename?: string;\n }): Promise<void> {\n try {\n await this.platform.fileSystem.deleteFile(uri);\n } catch (error) {\n // Ignore errors if file doesn't exist\n const info = await this.platform.fileSystem.getFileInfo(uri);\n if (info?.exists) {\n throw error;\n }\n }\n }\n\n /**\n * Check if a local file exists.\n */\n async fileExists(fileUri: string): Promise<boolean> {\n const info = await this.platform.fileSystem.getFileInfo(fileUri);\n return info?.exists ?? false;\n }\n\n /**\n * Create a directory (with intermediate directories).\n */\n async makeDir(uri: string): Promise<void> {\n await this.platform.fileSystem.makeDirectory(uri, {\n intermediates: true\n });\n }\n\n /**\n * Copy a file from source to destination.\n */\n async copyFile(sourceUri: string, targetUri: string): Promise<void> {\n await this.platform.fileSystem.copyFile(sourceUri, targetUri);\n }\n\n /**\n * Get the user's storage directory for attachments.\n * Returns the cache directory path ending with '/'.\n */\n getUserStorageDirectory(): string {\n return this.userStorageDirectory;\n }\n\n // ─── Helpers ────────────────────────────────────────────────────────────────\n\n /**\n * Warning 8: Optimized base64 encoding using batch processing.\n * Processes in chunks of 1024 bytes for better performance with large files.\n */\n private _arrayBufferToBase64(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer);\n const chunkSize = 1024;\n const chunks: string[] = [];\n for (let i = 0; i < bytes.length; i += chunkSize) {\n const chunk = bytes.subarray(i, Math.min(i + chunkSize, bytes.length));\n chunks.push(String.fromCharCode.apply(null, Array.from(chunk)));\n }\n return btoa(chunks.join(''));\n }\n private _base64ToArrayBuffer(base64: string): ArrayBuffer {\n const binaryString = atob(base64);\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 * Attachment Queue Types for @pol-studios/powersync\n *\n * This module re-exports types from the official @powersync/attachments package\n * and extends them with POL-specific features like:\n * - FAILED_PERMANENT state for permanent upload failures\n * - Upload metadata fields for retry tracking\n * - Source table configuration for flexible attachment watching\n * - Image compression configuration\n *\n * IMPORTANT: The official AttachmentState enum has different numeric values:\n * Official: QUEUED_SYNC=0, QUEUED_UPLOAD=1, QUEUED_DOWNLOAD=2, SYNCED=3, ARCHIVED=4\n *\n * We extend this with FAILED_PERMANENT=5 for permanent upload failures.\n */\n\n// ─── Re-exports from @powersync/attachments ──────────────────────────────────\n\n// Official types (import with explicit rename to avoid conflicts with our extensions)\nexport { AttachmentState, ATTACHMENT_TABLE, AttachmentTable, type AttachmentRecord as OfficialAttachmentRecord, type AttachmentTableOptions } from '@powersync/attachments';\nexport { EncodingType, type StorageAdapter } from '@powersync/attachments';\nexport { type AttachmentQueueOptions as BaseAttachmentQueueOptions, DEFAULT_ATTACHMENT_QUEUE_OPTIONS, AbstractAttachmentQueue } from '@powersync/attachments';\n\n// ─── POL Extended Attachment State ───────────────────────────────────────────\n\n/**\n * Extended attachment state that includes permanent failure state.\n *\n * Values 0-4 match the official @powersync/attachments AttachmentState.\n * Value 5 (FAILED_PERMANENT) is our extension for uploads that have\n * exhausted retries or encountered unrecoverable errors.\n *\n * @example\n * ```typescript\n * import { AttachmentState } from '@powersync/attachments';\n * import { PolAttachmentState } from '@pol-studios/powersync/attachments';\n *\n * // Use official states\n * const queued = AttachmentState.QUEUED_UPLOAD;\n *\n * // Use our extension\n * const failed = PolAttachmentState.FAILED_PERMANENT;\n * ```\n */\nexport enum PolAttachmentState {\n /** Check if the attachment needs to be uploaded or downloaded */\n QUEUED_SYNC = 0,\n /** Attachment to be uploaded */\n QUEUED_UPLOAD = 1,\n /** Attachment to be downloaded */\n QUEUED_DOWNLOAD = 2,\n /** Attachment has been synced */\n SYNCED = 3,\n /** Attachment has been orphaned, i.e. the associated record has been deleted */\n ARCHIVED = 4,\n /** Permanently failed (exhausted retries or unrecoverable error) - POL extension */\n FAILED_PERMANENT = 5,\n}\n\n// ─── POL Extended Attachment Record ──────────────────────────────────────────\n\n/**\n * Extended attachment record with POL-specific upload tracking fields.\n *\n * Extends the official AttachmentRecord with fields for:\n * - Upload retry tracking\n * - Error information\n * - Upload metadata for onComplete callbacks\n */\nexport interface PolAttachmentRecord {\n /** Unique identifier (typically storage path) */\n id: string;\n /** Filename for display and type inference */\n filename: string;\n /** Local file URI (set after download/upload) */\n local_uri?: string | null;\n /** File size in bytes */\n size?: number;\n /** MIME type of the file */\n media_type?: string;\n /** Timestamp when the attachment was created/modified */\n timestamp?: number;\n /** Current state in the queue */\n state: number; // AttachmentState or PolAttachmentState\n\n // ─── Upload-specific fields (POL extensions) ────────────────────────────────\n\n /** Local file URI in managed cache (for uploads) */\n upload_source_uri?: string | null;\n /** Last upload error message */\n upload_error?: string | null;\n /** HTTP status or error code */\n upload_error_code?: string | null;\n /** Number of upload retry attempts */\n upload_retry_count?: number;\n /** Timestamp for next retry */\n upload_next_retry_at?: number;\n /** JSON-serialized entity metadata for onComplete */\n upload_metadata?: string | null;\n /** Target storage bucket ID */\n upload_bucket_id?: string | null;\n}\n\n// ─── Source Table Configuration ──────────────────────────────────────────────\n\n/**\n * Configuration for the source table that contains attachment references.\n * This makes the attachment queue reusable across different tables/projects.\n */\nexport interface AttachmentSourceConfig {\n /**\n * Table name containing attachment references.\n * @example \"EquipmentUnitMediaContent\"\n */\n table: string;\n\n /**\n * Column containing the storage path / attachment ID.\n * @example \"storagePath\"\n */\n idColumn: string;\n\n /**\n * Column to order by for \"newest first\" downloads.\n * Set to null to skip ordering.\n * @example \"takenOn\"\n */\n orderByColumn?: string | null;\n\n /**\n * Optional filter config to exclude attachments from archived/unsynced projects.\n * When set, the attachment query JOINs through intermediary tables to ensure\n * only attachments belonging to synced projects are downloaded.\n */\n projectFilter?: {\n /** Foreign key column in the source table (e.g., \"equipmentUnitId\") */\n foreignKey: string;\n /** Intermediary table to JOIN through (e.g., \"EquipmentFixtureUnit\") */\n intermediaryTable: string;\n /** Column in intermediary table that links to ProjectDatabase (e.g., \"projectDatabaseId\") */\n projectForeignKey: string;\n };\n}\n\n// ─── Storage Adapter ─────────────────────────────────────────────────────────\n\n/**\n * Interface for remote attachment storage operations (e.g., Supabase Storage).\n *\n * This is used by PolStorageAdapter to handle remote file operations.\n * For local file operations, we use PlatformAdapter.fileSystem.\n */\nexport interface AttachmentStorageAdapter {\n /**\n * Download a file from remote storage.\n * @param filePath - The storage path of the file\n * @returns The file data as a Blob or base64 string\n */\n downloadFile(filePath: string): Promise<Blob | string>;\n\n /**\n * Upload a file to remote storage (optional - not all queues need upload).\n * @param filePath - The storage path to upload to\n * @param data - The file data to upload (base64 string or Blob)\n */\n uploadFile?(filePath: string, data: Blob | string): Promise<void>;\n\n /**\n * Delete a file from remote storage (optional).\n * @param filePath - The storage path of the file\n */\n deleteFile?(filePath: string): Promise<void>;\n\n /**\n * Resolve the storage bucket for a file path.\n * Allows routing different files to different buckets.\n * @param filePath - The file path to resolve\n * @returns The bucket name\n */\n resolveBucket?(filePath: string): string;\n}\n\n// ─── Compression Configuration ───────────────────────────────────────────────\n\n/**\n * Configuration for image compression.\n */\nexport interface CompressionConfig {\n /** Enable compression (default: true) */\n enabled: boolean;\n /** Compression quality 0.0-1.0 (default: 0.7) */\n quality: number;\n /** Max width before resizing (default: 2048) */\n maxWidth: number;\n /** Skip files under this size in bytes (default: 100KB) */\n skipSizeBytes: number;\n /** Skip if already under this size in bytes (default: 300KB) */\n targetSizeBytes: number;\n}\n\n/**\n * Default compression configuration.\n */\nexport const DEFAULT_COMPRESSION_CONFIG: CompressionConfig = {\n enabled: true,\n quality: 0.7,\n maxWidth: 2048,\n skipSizeBytes: 100_000,\n targetSizeBytes: 300_000\n};\n\n// ─── Upload Configuration ────────────────────────────────────────────────────\n\n/**\n * Configuration for the upload engine.\n */\nexport interface UploadConfig {\n /** Maximum concurrent uploads (default: 3) */\n concurrency: number;\n /** Upload timeout per file in ms (default: 120000) */\n timeoutMs: number;\n /** Base retry delay in ms (default: 5000) */\n baseRetryDelayMs: number;\n /** Maximum retry delay in ms (default: 3600000 = 1 hour) */\n maxRetryDelayMs: number;\n /** Days before marking upload as stale (default: 7) */\n staleDaysThreshold: number;\n /** Maximum number of retry attempts before marking as FAILED_PERMANENT (default: 100) */\n maxRetryCount: number;\n}\n\n/**\n * Default upload configuration.\n *\n * Note: baseRetryDelayMs is set to 30s (not 5s) to reduce battery consumption.\n * The exponential backoff sequence will be: 30s, 60s, 2min, 4min, 8min, 16min, 32min, 1hr (capped).\n */\nexport const DEFAULT_UPLOAD_CONFIG: UploadConfig = {\n concurrency: 3,\n timeoutMs: 120_000,\n baseRetryDelayMs: 30_000,\n // 30 seconds base delay for battery efficiency\n maxRetryDelayMs: 3_600_000,\n // 1 hour max\n staleDaysThreshold: 7,\n maxRetryCount: 100\n};\n\n// ─── Upload Handler ──────────────────────────────────────────────────────────\n\n/**\n * Interface for handling file uploads to remote storage.\n * Separate from AttachmentStorageAdapter to allow different implementations.\n */\nexport interface UploadHandler {\n /**\n * Upload a file to remote storage.\n * @param storagePath - The storage path to upload to\n * @param localFileUri - Local file URI to upload from\n * @param mediaType - MIME type of the file\n * @param signal - Optional AbortSignal for cancellation support\n */\n uploadFile(storagePath: string, localFileUri: string, mediaType: string, signal?: AbortSignal): Promise<void>;\n\n /**\n * Optional: resolve the storage bucket for a file path.\n * @param storagePath - The file path to resolve\n * @returns The bucket name\n */\n resolveBucket?(storagePath: string): string;\n}\n\n// ─── Cache Configuration ─────────────────────────────────────────────────────\n\n/**\n * Configuration for cache management.\n */\nexport interface CacheConfig {\n /** Maximum cache size in bytes (default: 5GB) */\n maxSize: number;\n /** Stop downloads at this percentage of max (default: 0.95 = 95%) */\n downloadStopThreshold: number;\n /** Trigger eviction at this percentage (default: 1.0 = 100%) */\n evictionTriggerThreshold: number;\n}\n\n/**\n * Default cache configuration.\n */\nexport const DEFAULT_CACHE_CONFIG: CacheConfig = {\n maxSize: 5 * 1024 * 1024 * 1024,\n // 5 GB\n downloadStopThreshold: 0.95,\n evictionTriggerThreshold: 1.0\n};\n\n// ─── POL Attachment Queue Configuration ──────────────────────────────────────\n\n/**\n * Configuration for the POL attachment queue.\n * Extends the official @powersync/attachments options with our features.\n */\nexport interface PolAttachmentQueueConfig {\n /** Source table configuration for watching attachment IDs */\n source: AttachmentSourceConfig;\n\n /** Remote storage adapter for downloading files */\n remoteStorage: AttachmentStorageAdapter;\n\n /** Table name for storing attachment records (default: \"attachments\") */\n attachmentTableName?: string;\n\n /** Perform initial sync on initialization (default: true) */\n performInitialSync?: boolean;\n\n /** Cache configuration */\n cache?: Partial<CacheConfig>;\n\n /** Compression configuration */\n compression?: Partial<CompressionConfig>;\n\n /**\n * Called when a download fails.\n * Return { retry: true } to retry, { retry: false } to skip.\n */\n onDownloadError?: (attachment: PolAttachmentRecord, error: Error) => Promise<{\n retry: boolean;\n }>;\n\n /**\n * Called when sync progress changes.\n */\n onProgress?: (stats: AttachmentSyncStats) => void;\n\n /** Upload configuration (optional - enables upload queue) */\n upload?: {\n handler: UploadHandler;\n config?: Partial<UploadConfig>;\n };\n\n /**\n * Called when an upload completes successfully.\n * Use this to create CRUD records after file upload.\n */\n onUploadComplete?: (record: PolAttachmentRecord) => Promise<void>;\n\n /**\n * Called when an upload fails permanently.\n */\n onUploadFailed?: (record: PolAttachmentRecord, error: Error) => void;\n}\n\n// ─── Download Status Types ───────────────────────────────────────────────────\n\n/**\n * Current phase of a download operation.\n */\nexport type DownloadPhase = 'downloading' | 'compressing' | 'complete' | 'error';\n\n/**\n * Status of an individual download.\n */\nexport interface DownloadStatus {\n /** Attachment ID */\n id: string;\n /** Filename being downloaded */\n filename: string;\n /** Current phase */\n phase: DownloadPhase;\n}\n\n// ─── Upload Status Types ─────────────────────────────────────────────────────\n\n/**\n * Current phase of an upload operation.\n */\nexport type UploadPhase = 'uploading' | 'waiting' | 'complete' | 'error' | 'permanent_failure';\n\n/**\n * Status of an individual upload.\n */\nexport interface UploadStatus {\n /** Attachment ID */\n id: string;\n /** Filename being uploaded */\n filename: string;\n /** Current phase */\n phase: UploadPhase;\n /** Upload progress (0-100) */\n progress?: number;\n /** Error message if failed */\n error?: string;\n}\n\n// ─── Sync Status Types ───────────────────────────────────────────────────────\n\n/**\n * Why downloads are stopped (if not actively syncing).\n */\nexport type AttachmentSyncStatus = 'syncing' // Actively downloading\n| 'paused' // User manually paused\n| 'cache_full' // Stopped because cache hit capacity\n| 'complete'; // All attachments downloaded\n\n/**\n * Statistics about the attachment sync progress.\n */\nexport interface AttachmentSyncStats {\n /** Number of attachments that have been downloaded */\n syncedCount: number;\n /** Total size of synced attachments in bytes */\n syncedSize: number;\n /** Number of attachments waiting to be downloaded */\n pendingCount: number;\n /** Total expected attachments (synced + pending) */\n totalExpected: number;\n /** Maximum cache size in bytes */\n maxCacheSize: number;\n /** Current compression quality (0.1 to 1.0) */\n compressionQuality: number;\n /** Current sync status */\n status: AttachmentSyncStatus;\n /** Whether downloads are paused */\n isPaused: boolean;\n /** Whether currently processing downloads */\n isProcessing: boolean;\n /** Currently active downloads */\n activeDownloads: DownloadStatus[];\n /** Number of uploads waiting to be processed */\n pendingUploadCount: number;\n /** Number of permanently failed uploads */\n failedPermanentCount: number;\n /** Number of uploads failing > staleDaysThreshold */\n staleUploadCount: number;\n /** Currently active uploads */\n activeUploads: UploadStatus[];\n}\n\n// ─── SQL Row Types (for internal use) ────────────────────────────────────────\n\n/** Row from stats query */\nexport interface AttachmentStatsRow {\n state: number;\n cnt: number;\n sz: number;\n}\n\n/** Row for cache file operations */\nexport interface CacheFileRow {\n id: string;\n local_uri: string;\n}\n\n/** Row for eviction operations */\nexport interface EvictRow {\n id: string;\n local_uri: string;\n size: number;\n}\n\n/** Row for cached size queries */\nexport interface CachedSizeRow {\n total: number;\n}\n\n/** Row for ID queries */\nexport interface IdRow {\n id: string;\n}\n\n/**\n * Alias for PolAttachmentRecord.\n * Use PolAttachmentRecord for new code. The official @powersync/attachments\n * AttachmentRecord is exported as OfficialAttachmentRecord.\n */\nexport type AttachmentRecord = PolAttachmentRecord;","/**\n * POL Attachment Queue\n *\n * Extends the official @powersync/attachments AbstractAttachmentQueue with\n * POL-specific features:\n * - Configurable source table watching for attachment IDs\n * - Project filtering for multi-tenant scenarios\n * - Durable upload queue with exponential backoff retry\n * - FAILED_PERMANENT state for unrecoverable upload errors\n * - Image compression integration\n * - Upload callbacks (onUploadComplete, onUploadFailed)\n *\n * @example\n * ```typescript\n * const queue = new PolAttachmentQueue({\n * powersync: db,\n * storage: storageAdapter,\n * source: {\n * table: 'EquipmentUnitMediaContent',\n * idColumn: 'storagePath',\n * orderByColumn: 'takenOn',\n * },\n * });\n *\n * await queue.init();\n * ```\n */\n\nimport { AbstractAttachmentQueue, AttachmentQueueOptions, AttachmentRecord, AttachmentState } from '@powersync/attachments';\nimport type { AbstractPowerSyncDatabase as PowerSyncDBInterface } from '@powersync/common';\nimport type { AbstractPowerSyncDatabase as PolPowerSyncDBInterface } from '../core/types';\nimport type { PlatformAdapter, LoggerAdapter } from '../platform/types';\nimport { PolStorageAdapter } from './pol-storage-adapter';\nimport { type AttachmentSourceConfig, type AttachmentStorageAdapter, type PolAttachmentRecord, type PolAttachmentQueueConfig, type UploadConfig, type UploadHandler, type UploadStatus, type AttachmentSyncStats, type DownloadStatus, type AttachmentSyncStatus, type CompressionConfig, type CacheConfig, PolAttachmentState, DEFAULT_UPLOAD_CONFIG, DEFAULT_COMPRESSION_CONFIG, DEFAULT_CACHE_CONFIG } from './types';\nimport { calculateBackoffDelay, addJitter } from '../utils/retry';\n\n// ─── Constants ───────────────────────────────────────────────────────────────\n\nconst WATCH_INTERVAL_MS = 30_000; // Poll for new attachments every 30s\nconst NOTIFY_THROTTLE_MS = 500;\nconst STATS_CACHE_TTL_MS = 500;\nconst UPLOAD_PROCESSING_DELAY_MS = 1000;\nconst MIN_POLL_INTERVAL_MS = 5_000; // Minimum sleep between upload polls when all items are in backoff\n\n// ─── Extended Options Interface ──────────────────────────────────────────────\n\n/**\n * Options for PolAttachmentQueue that extend the official AttachmentQueueOptions.\n */\nexport interface PolAttachmentQueueOptions extends AttachmentQueueOptions {\n /**\n * Platform adapter for file system and other platform operations.\n */\n platform: PlatformAdapter;\n\n /**\n * Remote storage adapter for upload/download operations.\n */\n remoteStorage: AttachmentStorageAdapter;\n\n /**\n * Source table configuration for watching attachment IDs.\n */\n source: AttachmentSourceConfig;\n\n /**\n * Upload handler for uploading files to remote storage.\n */\n uploadHandler?: UploadHandler;\n\n /**\n * Upload configuration.\n */\n uploadConfig?: Partial<UploadConfig>;\n\n /**\n * Called when an upload completes successfully.\n */\n onUploadComplete?: (record: PolAttachmentRecord) => Promise<void>;\n\n /**\n * Called when an upload fails permanently.\n */\n onUploadFailed?: (record: PolAttachmentRecord, error: Error) => void;\n\n /**\n * Compression configuration.\n */\n compression?: Partial<CompressionConfig>;\n\n /**\n * Cache configuration.\n */\n cache?: Partial<CacheConfig>;\n}\n\n/**\n * POL Attachment Queue that extends the official AbstractAttachmentQueue.\n *\n * This class adds:\n * - Configurable source table watching (not hardcoded to a single table)\n * - Project filtering for multi-tenant scenarios\n * - Durable upload queue with exponential backoff\n * - FAILED_PERMANENT state for permanent upload failures\n * - Image compression integration\n */\nexport class PolAttachmentQueue extends AbstractAttachmentQueue<PolAttachmentQueueOptions> {\n // ─── Parent Override ──────────────────────────────────────────────────────\n\n /**\n * Override parent's watchUploads to prevent dual upload engines.\n * POL uses custom _startUploadProcessing() for uploads with:\n * - Exponential backoff retry\n * - Compression support\n * - Custom metadata handling\n *\n * The parent's simple upload watcher would conflict and fail\n * because POL records use upload_source_uri instead of local_uri.\n */\n watchUploads(): void {\n // Intentionally disabled - POL handles uploads via _startUploadProcessing()\n this.polLogger?.debug?.('[PolAttachmentQueue] Parent watchUploads disabled - using custom upload engine');\n }\n\n /**\n * Override parent's uploadAttachment to prevent misuse.\n * POL uploads use upload_source_uri, not local_uri.\n * External code should use queueUpload() instead.\n */\n async uploadAttachment(record: {\n id: string;\n }): Promise<boolean> {\n throw new Error(`uploadAttachment() is disabled in PolAttachmentQueue. ` + `Use queueUpload() for uploads. Record ID: ${record.id}`);\n }\n\n // ─── Additional State ──────────────────────────────────────────────────────\n\n private readonly platform: PlatformAdapter;\n private readonly polLogger: LoggerAdapter;\n private readonly source: AttachmentSourceConfig;\n private readonly uploadHandler?: UploadHandler;\n private readonly uploadConfig: UploadConfig;\n private readonly compressionConfig: CompressionConfig;\n private readonly cacheConfig: CacheConfig;\n\n // Upload engine state\n private _uploadProcessing = false;\n private _uploadPaused = false;\n private _uploadAbort = new AbortController();\n private _activeUploads = new Map<string, UploadStatus>();\n\n // Watch state\n private _watchInterval: ReturnType<typeof setInterval> | null = null;\n private _disposed = false;\n private _initialized = false;\n\n // Notification state\n private _progressCallbacks = new Set<(stats: AttachmentSyncStats) => void>();\n private _lastNotifyTime = 0;\n private _notifyTimer: ReturnType<typeof setTimeout> | null = null;\n\n // Stats cache\n private _cachedStats: AttachmentSyncStats | null = null;\n private _cachedStatsTimestamp = 0;\n constructor(options: PolAttachmentQueueOptions) {\n // Create the PolStorageAdapter that implements the official interface\n const storage = new PolStorageAdapter({\n platform: options.platform,\n remoteStorage: options.remoteStorage,\n attachmentDirectoryName: options.attachmentDirectoryName\n });\n\n // Call parent constructor with our storage adapter\n super({\n ...options,\n storage\n });\n\n // Validate SQL identifiers to prevent injection (Warning 5)\n this._validateSqlIdentifier(options.source.table, 'source.table');\n this._validateSqlIdentifier(options.source.idColumn, 'source.idColumn');\n if (options.source.orderByColumn) {\n this._validateSqlIdentifier(options.source.orderByColumn, 'source.orderByColumn');\n }\n if (options.source.projectFilter) {\n this._validateSqlIdentifier(options.source.projectFilter.foreignKey, 'projectFilter.foreignKey');\n this._validateSqlIdentifier(options.source.projectFilter.intermediaryTable, 'projectFilter.intermediaryTable');\n this._validateSqlIdentifier(options.source.projectFilter.projectForeignKey, 'projectFilter.projectForeignKey');\n }\n this.platform = options.platform;\n this.polLogger = options.platform.logger;\n this.source = options.source;\n this.uploadHandler = options.uploadHandler;\n this.uploadConfig = {\n ...DEFAULT_UPLOAD_CONFIG,\n ...options.uploadConfig\n };\n this.compressionConfig = {\n ...DEFAULT_COMPRESSION_CONFIG,\n ...options.compression\n };\n this.cacheConfig = {\n ...DEFAULT_CACHE_CONFIG,\n ...options.cache\n };\n }\n\n /**\n * Validate that a string is a safe SQL identifier (Warning 5).\n * Prevents SQL injection via config values.\n */\n private _validateSqlIdentifier(value: string, name: string): void {\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(value)) {\n throw new Error(`[PolAttachmentQueue] Invalid SQL identifier for ${name}: \"${value}\". ` + `Must match /^[a-zA-Z_][a-zA-Z0-9_]*$/`);\n }\n }\n\n // ─── Abstract Method Implementations ───────────────────────────────────────\n\n /**\n * Watch the source table for attachment IDs and notify when they change.\n *\n * This implementation uses our configurable source table pattern rather\n * than a hardcoded query, supporting project filtering for multi-tenant apps.\n */\n onAttachmentIdsChange(onUpdate: (ids: string[]) => void): void {\n const {\n table,\n idColumn,\n projectFilter\n } = this.source;\n\n // Build the query based on configuration\n let query: string;\n if (projectFilter) {\n const {\n foreignKey,\n intermediaryTable,\n projectForeignKey\n } = projectFilter;\n query = `\n SELECT m.${idColumn} as id\n FROM ${table} m\n JOIN ${intermediaryTable} u ON m.${foreignKey} = u.id\n JOIN ProjectDatabase p ON u.${projectForeignKey} = p.id\n WHERE m.${idColumn} IS NOT NULL AND m.${idColumn} != ''\n `;\n } else {\n query = `\n SELECT ${idColumn} as id\n FROM ${table}\n WHERE ${idColumn} IS NOT NULL AND ${idColumn} != ''\n `;\n }\n\n // Use PowerSync watch API for reactive updates\n try {\n this.powersync.watch(query, [], {\n onResult: result => {\n const ids = result.rows?._array?.map((r: {\n id: string;\n }) => r.id) ?? [];\n onUpdate(ids);\n },\n onError: error => {\n // Expected during initial sync - table may not exist yet\n const errorMessage = String(error);\n if (errorMessage.includes('no such table') || errorMessage.includes('SQLITE_EMPTY')) {\n this.polLogger.debug('[PolAttachmentQueue] Source table not ready yet');\n return;\n }\n this.polLogger.warn('[PolAttachmentQueue] Watch error:', error);\n }\n });\n } catch (error) {\n // Fallback to interval-based polling if watch fails\n this.polLogger.warn('[PolAttachmentQueue] Watch failed, falling back to polling:', error);\n this._startPolling(query, onUpdate);\n }\n }\n\n /**\n * Create a new attachment record with POL-specific defaults.\n */\n async newAttachmentRecord(record?: Partial<AttachmentRecord>): Promise<AttachmentRecord> {\n // Additional Issue: Guard against empty id\n if (!record?.id) {\n throw new Error('[PolAttachmentQueue] newAttachmentRecord requires a non-empty record.id');\n }\n const id = record.id;\n const filename = record?.filename ?? id;\n return {\n id,\n filename,\n state: record?.state ?? AttachmentState.QUEUED_SYNC,\n local_uri: record?.local_uri,\n size: record?.size,\n media_type: record?.media_type,\n timestamp: record?.timestamp ?? Date.now()\n };\n }\n\n // ─── Lifecycle ─────────────────────────────────────────────────────────────\n\n /**\n * Initialize the attachment queue.\n *\n * Extends parent init() to also:\n * - Create the attachment table (since we don't use AttachmentTable in Schema)\n * - Add upload-specific columns to the table\n * - Start upload processing if handler is configured\n */\n async init(): Promise<void> {\n // Create the table if it doesn't exist\n // NOTE: We do NOT add AttachmentTable to the PowerSync Schema because that creates\n // a VIEW (not a real table), which doesn't support INSERT/UPDATE operations.\n // Instead, we create a real SQLite table here.\n await this._createTableIfNotExists();\n\n // Call parent init (sets up watchers, directory, etc.)\n await super.init();\n\n // Mark as initialized AFTER table is created\n this._initialized = true;\n\n // Add upload-specific columns if they don't exist\n await this._migrateUploadColumns();\n\n // Start upload processing if handler is configured\n if (this.uploadHandler && !this._uploadPaused) {\n this._startUploadProcessing();\n }\n this.polLogger.info('[PolAttachmentQueue] Initialized');\n }\n\n /**\n * Create the attachment table if it doesn't exist.\n * This creates a REAL SQLite table (not a view) that supports all SQL operations.\n */\n private async _createTableIfNotExists(): Promise<void> {\n // Check if table exists\n const result = await this.powersync.getOptional<{\n name: string;\n }>(`SELECT name FROM sqlite_master WHERE type='table' AND name=?`, [this.table]);\n if (result) {\n this.polLogger.debug(`[PolAttachmentQueue] Table ${this.table} already exists`);\n return;\n }\n this.polLogger.info(`[PolAttachmentQueue] Creating table ${this.table}`);\n\n // Create table with all required columns (base + upload-specific)\n await this.powersync.execute(`\n CREATE TABLE IF NOT EXISTS ${this.table} (\n id TEXT PRIMARY KEY NOT NULL,\n filename TEXT,\n local_uri TEXT,\n timestamp INTEGER,\n media_type TEXT,\n size INTEGER,\n state INTEGER DEFAULT 0,\n upload_source_uri TEXT,\n upload_error TEXT,\n upload_error_code TEXT,\n upload_retry_count INTEGER DEFAULT 0,\n upload_next_retry_at INTEGER,\n upload_metadata TEXT,\n upload_bucket_id TEXT\n )\n `);\n this.polLogger.info(`[PolAttachmentQueue] Table ${this.table} created`);\n }\n\n /**\n * Dispose the attachment queue, cleaning up all resources.\n */\n dispose(): void {\n this._disposed = true;\n this._uploadAbort.abort();\n if (this._watchInterval) {\n clearInterval(this._watchInterval);\n this._watchInterval = null;\n }\n if (this._notifyTimer) {\n clearTimeout(this._notifyTimer);\n this._notifyTimer = null;\n }\n this._progressCallbacks.clear();\n this._activeUploads.clear();\n\n // Blocker 2: Call parent dispose if it exists\n // The parent AbstractAttachmentQueue sets up an interval in init() that needs cleanup\n const parentProto = Object.getPrototypeOf(Object.getPrototypeOf(this));\n if (parentProto && typeof parentProto.dispose === 'function') {\n parentProto.dispose.call(this);\n }\n }\n\n // ─── Upload API ────────────────────────────────────────────────────────────\n\n /**\n * Queue a file for upload.\n *\n * The file is immediately copied to managed cache for durability.\n * Upload will retry indefinitely for transient errors until marked\n * as FAILED_PERMANENT for unrecoverable errors.\n */\n async queueUpload(options: {\n storagePath: string;\n sourceUri: string;\n filename: string;\n mediaType: string;\n bucketId?: string;\n metadata?: Record<string, unknown>;\n }): Promise<void> {\n if (!this.uploadHandler) {\n throw new Error('[PolAttachmentQueue] Upload not configured. Provide uploadHandler in options.');\n }\n if (!this._initialized) {\n throw new Error('[PolAttachmentQueue] Queue not initialized. Call init() first or wait for attachmentQueueReady.');\n }\n\n // 1. Copy source file to managed cache immediately for durability\n const managedUri = await this._copyToManagedCache(options.sourceUri, options.storagePath);\n\n // 2. Get file size\n const fileInfo = await this.platform.fileSystem.getFileInfo(managedUri);\n const size = fileInfo?.exists ? fileInfo.size : 0;\n\n // 3. Insert record with QUEUED_UPLOAD state\n // Use ON CONFLICT to preserve existing local_uri/size if a download record already exists\n const insertParams = [options.storagePath,\n // id = storage path\n options.filename, options.mediaType, AttachmentState.QUEUED_UPLOAD, null,\n // local_uri is for downloads (preserved on conflict)\n size, Date.now(), managedUri,\n // upload_source_uri\n 0,\n // upload_retry_count\n Date.now(),\n // upload_next_retry_at (ready now)\n options.metadata ? JSON.stringify(options.metadata) : null, options.bucketId || null];\n this.polLogger.info(`[PolAttachmentQueue] queueUpload params:`, {\n storagePath: options.storagePath,\n filename: options.filename,\n mediaType: options.mediaType,\n size,\n managedUri,\n bucketId: options.bucketId,\n metadataLength: options.metadata ? JSON.stringify(options.metadata).length : 0\n });\n try {\n // Use INSERT OR REPLACE instead of UPSERT syntax - PowerSync views don't support ON CONFLICT\n await this.powersync.execute(`INSERT OR REPLACE INTO ${this.table}\n (id, filename, media_type, state, local_uri, size, timestamp,\n upload_source_uri, upload_retry_count, upload_next_retry_at,\n upload_metadata, upload_bucket_id)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, insertParams);\n } catch (insertError) {\n this.polLogger.error(`[PolAttachmentQueue] queueUpload INSERT failed:`, insertError);\n throw insertError;\n }\n this.polLogger.info(`[PolAttachmentQueue] Queued upload: ${options.storagePath}`);\n\n // 4. Trigger upload processing\n if (!this._uploadPaused && !this._uploadProcessing) {\n this._startUploadProcessing();\n }\n this._notify(true);\n }\n\n /**\n * Get pending uploads that need retry.\n */\n async getPendingUploads(): Promise<PolAttachmentRecord[]> {\n const now = Date.now();\n return this.powersync.getAll<PolAttachmentRecord>(`SELECT * FROM ${this.table}\n WHERE state = ? AND (upload_next_retry_at IS NULL OR upload_next_retry_at <= ?)\n ORDER BY timestamp ASC`, [AttachmentState.QUEUED_UPLOAD, now]);\n }\n\n /**\n * Get the soonest retry time for all uploads currently in backoff.\n * Returns null if there are no uploads waiting for retry.\n */\n async getSoonestRetryTime(): Promise<number | null> {\n const now = Date.now();\n const result = await this.powersync.getOptional<{\n soonest: number | null;\n }>(`SELECT MIN(upload_next_retry_at) as soonest FROM ${this.table}\n WHERE state = ? AND upload_next_retry_at > ?`, [AttachmentState.QUEUED_UPLOAD, now]);\n return result?.soonest ?? null;\n }\n\n /**\n * Get uploads that have permanently failed.\n */\n async getFailedPermanentUploads(): Promise<PolAttachmentRecord[]> {\n return this.powersync.getAll<PolAttachmentRecord>(`SELECT * FROM ${this.table} WHERE state = ?`, [PolAttachmentState.FAILED_PERMANENT]);\n }\n\n /**\n * Get stale uploads (failing for > staleDaysThreshold).\n */\n async getStaleUploads(): Promise<PolAttachmentRecord[]> {\n const threshold = Date.now() - this.uploadConfig.staleDaysThreshold * 24 * 60 * 60 * 1000;\n return this.powersync.getAll<PolAttachmentRecord>(`SELECT * FROM ${this.table}\n WHERE state = ? AND timestamp < ?`, [AttachmentState.QUEUED_UPLOAD, threshold]);\n }\n\n /**\n * Get SYNCED uploads that have pending onComplete callbacks.\n * Used by useQueuedUpload to recover callbacks after unmount/remount.\n *\n * Records are identified by having 'onCompleteCallback' flag in their upload_metadata.\n * Once the callback is invoked, the hook should call clearUploadCallback() to remove the flag.\n */\n async getSyncedUploadsWithPendingCallback(): Promise<PolAttachmentRecord[]> {\n // SQLite JSON functions: use json_extract to check for the onCompleteCallback flag\n // Note: upload_metadata is a JSON string, so we check if it contains the callback flag\n return this.powersync.getAll<PolAttachmentRecord>(`SELECT * FROM ${this.table}\n WHERE state = ? AND upload_metadata LIKE '%\"onCompleteCallback\":true%'`, [AttachmentState.SYNCED]);\n }\n\n /**\n * Clear the onCompleteCallback flag from a record's metadata after callback invocation.\n */\n async clearUploadCallback(id: string): Promise<void> {\n const record = await this.record(id);\n if (!record) return;\n const polRecord = record as unknown as PolAttachmentRecord;\n if (!polRecord.upload_metadata) return;\n try {\n const metadata = JSON.parse(polRecord.upload_metadata) as Record<string, unknown>;\n delete metadata.onCompleteCallback;\n await this.powersync.execute(`UPDATE ${this.table} SET upload_metadata = ? WHERE id = ?`, [JSON.stringify(metadata), id]);\n } catch {\n // If parsing fails, just clear the whole metadata\n await this.powersync.execute(`UPDATE ${this.table} SET upload_metadata = NULL WHERE id = ?`, [id]);\n }\n }\n\n /**\n * Retry a failed upload.\n */\n async retryUpload(id: string): Promise<void> {\n await this.powersync.execute(`UPDATE ${this.table}\n SET state = ?, upload_retry_count = 0, upload_next_retry_at = ?, upload_error = NULL\n WHERE id = ?`, [AttachmentState.QUEUED_UPLOAD, Date.now(), id]);\n if (!this._uploadPaused && !this._uploadProcessing) {\n this._startUploadProcessing();\n }\n this._notify(true);\n }\n\n /**\n * Delete an upload from the queue.\n */\n async deleteUpload(id: string): Promise<void> {\n const record = await this.record(id);\n if (record) {\n const polRecord = record as unknown as PolAttachmentRecord;\n if (polRecord.upload_source_uri) {\n try {\n await this.platform.fileSystem.deleteFile(polRecord.upload_source_uri);\n } catch {\n // File may already be gone\n }\n }\n }\n await this.powersync.execute(`DELETE FROM ${this.table} WHERE id = ?`, [id]);\n this._notify(true);\n }\n\n // ─── Compatibility Aliases ────────────────────────────────────────────────────\n\n /**\n * Alias for record() for backward compatibility.\n * Returns an attachment record by ID.\n */\n async getRecord(id: string): Promise<PolAttachmentRecord | null> {\n const record = await this.record(id);\n return record as unknown as PolAttachmentRecord | null;\n }\n\n /**\n * Alias for getFailedPermanentUploads() for backward compatibility.\n */\n async getFailedUploads(): Promise<PolAttachmentRecord[]> {\n return this.getFailedPermanentUploads();\n }\n\n /**\n * Alias for retryUpload() for backward compatibility.\n */\n async retryFailedUpload(id: string): Promise<void> {\n return this.retryUpload(id);\n }\n\n /**\n * Alias for deleteUpload() for backward compatibility.\n */\n async deleteFailedUpload(id: string): Promise<void> {\n return this.deleteUpload(id);\n }\n\n /**\n * Get currently active uploads.\n */\n get activeUploads(): UploadStatus[] {\n return [...this._activeUploads.values()];\n }\n\n /**\n * Pause upload processing.\n */\n pauseUploads(): void {\n this.polLogger.info('[PolAttachmentQueue] Pausing uploads');\n this._uploadPaused = true;\n this._uploadAbort.abort();\n this._uploadAbort = new AbortController();\n this._notify(true);\n }\n\n /**\n * Resume upload processing.\n */\n resumeUploads(): void {\n this.polLogger.info('[PolAttachmentQueue] Resuming uploads');\n this._uploadPaused = false;\n this._uploadAbort = new AbortController();\n if (!this._uploadProcessing) {\n this._startUploadProcessing();\n }\n this._notify(true);\n }\n\n // ─── Cache Management ──────────────────────────────────────────────────────\n\n /**\n * Clear all cached files and re-queue for download.\n */\n async clearCache(): Promise<void> {\n this.polLogger.info('[PolAttachmentQueue] Clearing cache...');\n\n // Get only download-related records with local files\n // Exclude upload records (QUEUED_UPLOAD, FAILED_PERMANENT) to preserve upload source files\n const records = await this.powersync.getAll<{\n id: string;\n local_uri: string;\n }>(`SELECT id, local_uri FROM ${this.table}\n WHERE local_uri IS NOT NULL\n AND state IN (?, ?, ?)`, [AttachmentState.QUEUED_DOWNLOAD, AttachmentState.SYNCED, AttachmentState.QUEUED_SYNC]);\n\n // Delete local files\n for (const record of records) {\n if (record.local_uri) {\n try {\n const fullPath = this.getLocalUri(record.local_uri);\n await this.platform.fileSystem.deleteFile(fullPath);\n } catch {\n // File may already be gone\n }\n }\n }\n\n // Reset only download-related records to QUEUED_DOWNLOAD\n // Preserve upload records (QUEUED_UPLOAD, FAILED_PERMANENT) to avoid losing pending uploads\n await this.powersync.execute(`UPDATE ${this.table}\n SET state = ?, local_uri = NULL, size = 0\n WHERE state IN (?, ?, ?)`, [AttachmentState.QUEUED_DOWNLOAD, AttachmentState.QUEUED_DOWNLOAD, AttachmentState.SYNCED, AttachmentState.QUEUED_SYNC]);\n\n // Invalidate stats cache\n this._cachedStats = null;\n this._cachedStatsTimestamp = 0;\n this._notify(true);\n this.polLogger.info('[PolAttachmentQueue] Cache cleared');\n }\n\n /**\n * Cache a local file (e.g., one just uploaded) into the attachment cache.\n * This avoids a redundant download by copying the source file directly\n * into the cache directory and marking it as SYNCED.\n */\n async cacheLocalFile(storagePath: string, sourceUri: string): Promise<void> {\n try {\n // Verify source file exists\n const sourceInfo = await this.platform.fileSystem.getFileInfo(sourceUri);\n if (!sourceInfo || !sourceInfo.exists) {\n this.polLogger.warn(`[PolAttachmentQueue] Source file does not exist: ${sourceUri}`);\n return;\n }\n\n // Blocker 1: Use consistent path sanitization (preserves dots and hyphens like _copyToManagedCache)\n const localPath = storagePath.replace(/[^a-zA-Z0-9._-]/g, '_');\n const localUri = this.getLocalUri(localPath);\n\n // Ensure cache directory exists\n const dir = localUri.substring(0, localUri.lastIndexOf('/'));\n await this.platform.fileSystem.makeDirectory(dir, {\n intermediates: true\n });\n\n // Copy uploaded file into cache\n await this.platform.fileSystem.copyFile(sourceUri, localUri);\n\n // Get file size for cache tracking\n const info = await this.platform.fileSystem.getFileInfo(localUri);\n const size = info && info.exists ? info.size : 0;\n\n // Infer media type from extension\n const ext = storagePath.split('.').pop()?.toLowerCase() ?? '';\n const mediaTypeMap: Record<string, string> = {\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n png: 'image/png',\n webp: 'image/webp',\n heic: 'image/heic',\n heif: 'image/heif',\n gif: 'image/gif',\n mp4: 'video/mp4',\n mov: 'video/quicktime'\n };\n const mediaType = mediaTypeMap[ext] ?? 'application/octet-stream';\n\n // Insert as SYNCED, preserving upload metadata columns on conflict\n await this.powersync.execute(`INSERT INTO ${this.table} (id, filename, media_type, state, local_uri, size, timestamp)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(id) DO UPDATE SET\n local_uri = excluded.local_uri,\n size = excluded.size,\n state = excluded.state,\n timestamp = excluded.timestamp`, [storagePath, storagePath, mediaType, AttachmentState.SYNCED, localPath, size, Date.now()]);\n\n // Invalidate stats cache\n this._cachedStats = null;\n this._cachedStatsTimestamp = 0;\n this._notify(true);\n this.polLogger.info(`[PolAttachmentQueue] Cached local file: ${storagePath} (${Math.round(size / 1024)}KB)`);\n } catch (err) {\n this.polLogger.warn(`[PolAttachmentQueue] Failed to cache local file: ${storagePath}`, err);\n }\n }\n\n /**\n * Get the local file URI for a storage path.\n * Checks both downloaded files (SYNCED state, local_uri) and\n * pending uploads (QUEUED_UPLOAD state, upload_source_uri).\n *\n * @param storagePath - The storage path (e.g., \"14/uuid.jpg\")\n * @returns Full file:// URI if available locally, null otherwise\n */\n async getLocalUriForStoragePath(storagePath: string): Promise<string | null> {\n // Query both SYNCED downloads and QUEUED_UPLOAD pending uploads\n let result: {\n local_uri: string | null;\n upload_source_uri: string | null;\n state: number;\n } | null = null;\n try {\n result = await this.powersync.getOptional<{\n local_uri: string | null;\n upload_source_uri: string | null;\n state: number;\n }>(`SELECT local_uri, upload_source_uri, state FROM ${this.table}\n WHERE id = ? AND (\n (state = ? AND local_uri IS NOT NULL) OR\n (state = ? AND upload_source_uri IS NOT NULL)\n )`, [storagePath, AttachmentState.SYNCED, AttachmentState.QUEUED_UPLOAD]);\n } catch (queryError) {\n this.polLogger.error(`[PolAttachmentQueue] getLocalUriForStoragePath query failed:`, {\n storagePath,\n syncedState: AttachmentState.SYNCED,\n queuedUploadState: AttachmentState.QUEUED_UPLOAD,\n error: queryError\n });\n throw queryError;\n }\n if (!result) return null;\n\n // Determine which URI to use based on state\n let localPath: string | null = null;\n if (result.state === AttachmentState.SYNCED && result.local_uri) {\n localPath = result.local_uri;\n } else if (result.state === AttachmentState.QUEUED_UPLOAD && result.upload_source_uri) {\n // For pending uploads, upload_source_uri is already a full path\n const uri = result.upload_source_uri.startsWith('file://') ? result.upload_source_uri : `file://${result.upload_source_uri}`;\n\n // Verify file exists\n try {\n const exists = await this.storage.fileExists(uri.replace('file://', ''));\n if (!exists) {\n this.polLogger.warn(`[PolAttachmentQueue] Upload source file missing: ${storagePath}`);\n return null;\n }\n } catch (e) {\n this.polLogger.warn(`[PolAttachmentQueue] Failed to verify upload source exists: ${storagePath}`, e);\n }\n return uri;\n }\n if (!localPath) return null;\n\n // getLocalUri returns the full path, ensure it has file:// prefix\n const fullPath = this.getLocalUri(localPath);\n const uri = fullPath.startsWith('file://') ? fullPath : `file://${fullPath}`;\n\n // Verify file actually exists on disk\n try {\n const exists = await this.storage.fileExists(uri.replace('file://', ''));\n if (!exists) {\n this.polLogger.warn(`[PolAttachmentQueue] Cached file missing from disk, re-queuing: ${storagePath}`);\n await this.powersync.execute(`UPDATE ${this.table} SET state = ?, local_uri = NULL WHERE id = ?`, [AttachmentState.QUEUED_DOWNLOAD, storagePath]);\n return null;\n }\n } catch (e) {\n // If we can't check, assume file exists to avoid unnecessary re-downloads\n this.polLogger.warn(`[PolAttachmentQueue] Failed to verify file exists: ${storagePath}`, e);\n }\n return uri;\n }\n\n // ─── Progress Subscription ─────────────────────────────────────────────────\n\n /**\n * Subscribe to real-time progress updates.\n * Returns an unsubscribe function.\n */\n onProgress(callback: (stats: AttachmentSyncStats) => void): () => void {\n this._progressCallbacks.add(callback);\n // Immediately notify with current stats\n this._notify(true);\n return () => {\n this._progressCallbacks.delete(callback);\n };\n }\n\n /**\n * Get current sync statistics.\n */\n async getStats(): Promise<AttachmentSyncStats> {\n const now = Date.now();\n\n // Return cached stats if still valid\n if (this._cachedStats && now - this._cachedStatsTimestamp < STATS_CACHE_TTL_MS) {\n return {\n ...this._cachedStats,\n status: this._getStatus(this._cachedStats.pendingCount),\n isPaused: this._uploadPaused,\n isProcessing: this.uploading || this.downloading || this._uploadProcessing,\n activeDownloads: [],\n activeUploads: this.activeUploads\n };\n }\n\n // Query for stats\n interface StatsRow {\n state: number;\n cnt: number;\n sz: number;\n }\n const rows = await this.powersync.getAll<StatsRow>(`SELECT state, COUNT(*) as cnt, COALESCE(SUM(size), 0) as sz\n FROM ${this.table} GROUP BY state`);\n let synced = 0;\n let syncedSize = 0;\n let pending = 0;\n let pendingUpload = 0;\n let failedPermanent = 0;\n for (const r of rows) {\n if (r.state === AttachmentState.SYNCED) {\n synced = r.cnt;\n syncedSize = r.sz;\n }\n if (r.state === AttachmentState.QUEUED_DOWNLOAD || r.state === AttachmentState.QUEUED_SYNC) {\n pending += r.cnt;\n }\n if (r.state === AttachmentState.QUEUED_UPLOAD) {\n pendingUpload = r.cnt;\n }\n if (r.state === PolAttachmentState.FAILED_PERMANENT) {\n failedPermanent = r.cnt;\n }\n }\n\n // Get stale uploads\n const staleUploads = await this.getStaleUploads();\n const stats: AttachmentSyncStats = {\n syncedCount: synced,\n syncedSize,\n pendingCount: pending,\n totalExpected: synced + pending,\n maxCacheSize: this.cacheConfig.maxSize,\n compressionQuality: this.compressionConfig.quality,\n status: this._getStatus(pending),\n isPaused: this._uploadPaused,\n isProcessing: this.uploading || this.downloading || this._uploadProcessing,\n activeDownloads: [],\n pendingUploadCount: pendingUpload,\n failedPermanentCount: failedPermanent,\n staleUploadCount: staleUploads.length,\n activeUploads: this.activeUploads\n };\n this._cachedStats = stats;\n this._cachedStatsTimestamp = now;\n return stats;\n }\n\n // ─── Upload Processing ─────────────────────────────────────────────────────\n\n private async _startUploadProcessing(): Promise<void> {\n if (this._uploadPaused || this._uploadProcessing || !this.uploadHandler) return;\n this.polLogger.info('[PolAttachmentQueue] Starting upload processing');\n this._uploadProcessing = true;\n try {\n while (!this._uploadAbort.signal.aborted) {\n const pending = await this.getPendingUploads();\n if (pending.length > 0) {\n // Process in batches with concurrency limit\n for (let i = 0; i < pending.length; i += this.uploadConfig.concurrency) {\n if (this._uploadAbort.signal.aborted) break;\n const batch = pending.slice(i, i + this.uploadConfig.concurrency);\n await Promise.allSettled(batch.map(record => this._uploadOne(record)));\n }\n\n // Small delay between batches to prevent tight loops\n await this._sleep(UPLOAD_PROCESSING_DELAY_MS);\n } else {\n // No uploads ready now - check if there are any waiting for retry\n const soonestRetry = await this.getSoonestRetryTime();\n if (soonestRetry === null) {\n // No uploads at all, exit the loop\n this.polLogger.info('[PolAttachmentQueue] No pending uploads, stopping processing');\n break;\n }\n\n // Calculate how long to sleep until the soonest retry\n const now = Date.now();\n const sleepDuration = Math.max(MIN_POLL_INTERVAL_MS, Math.min(soonestRetry - now, this.uploadConfig.maxRetryDelayMs));\n this.polLogger.info(`[PolAttachmentQueue] All uploads in backoff, sleeping ${Math.round(sleepDuration / 1000)}s until next retry`);\n await this._sleep(sleepDuration);\n }\n }\n } catch (error) {\n this.polLogger.error('[PolAttachmentQueue] Upload loop error:', error);\n } finally {\n this._uploadProcessing = false;\n this.polLogger.info('[PolAttachmentQueue] Upload processing stopped');\n this._notify(true);\n }\n }\n private async _uploadOne(record: PolAttachmentRecord): Promise<void> {\n const signal = this._uploadAbort.signal;\n if (signal.aborted) return;\n\n // Track active upload\n this._activeUploads.set(record.id, {\n id: record.id,\n filename: record.filename,\n phase: 'uploading'\n });\n try {\n // 1. Verify source file exists\n if (!record.upload_source_uri) {\n await this._markUploadPermanentFailure(record, 'No source file URI');\n return;\n }\n const fileInfo = await this.platform.fileSystem.getFileInfo(record.upload_source_uri);\n if (!fileInfo || !fileInfo.exists) {\n await this._markUploadPermanentFailure(record, 'Source file no longer available');\n return;\n }\n\n // 2. Attempt upload (Warning 6: pass abort signal for actual cancellation)\n await this._withTimeout(this.uploadHandler!.uploadFile(record.id, record.upload_source_uri, record.media_type || 'application/octet-stream', signal), this.uploadConfig.timeoutMs, 'Upload timed out', signal);\n\n // 3. Success - mark as synced\n await this._markUploadSynced(record);\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n\n // Don't count aborts (from pause/dispose) as failures\n // These will be re-processed when uploads resume\n if (err.name === 'AbortError' || err instanceof DOMException && err.name === 'AbortError') {\n this.polLogger?.debug?.('[PolAttachmentQueue] Upload aborted (pause/dispose), will retry on resume', {\n id: record.id\n });\n return;\n }\n if (this._isPermanentError(err)) {\n await this._markUploadPermanentFailure(record, err.message, this._extractErrorCode(err));\n } else {\n await this._scheduleUploadRetry(record, err);\n }\n } finally {\n this._activeUploads.delete(record.id);\n this._notify(false);\n }\n }\n private _isPermanentError(error: Error): boolean {\n const message = error.message.toLowerCase();\n const code = this._extractErrorCode(error);\n\n // Permanent HTTP status codes (4xx except 429)\n const permanentCodes = ['400', '401', '403', '404', '413', '415', '422'];\n if (code && permanentCodes.includes(code)) {\n return true;\n }\n\n // Blocker 3: Only match specific error phrases, not generic terms\n // Removed: 'invalid', 'bucket', 'policy' (too broad - could match transient errors)\n if (message.includes('not found') || message.includes('unauthorized') || message.includes('forbidden') || message.includes('access denied')) {\n return true;\n }\n return false;\n }\n\n /**\n * Extract HTTP status code from error message.\n * Uses specific patterns to avoid false positives from numbers in other contexts.\n */\n private _extractErrorCode(error: Error): string | undefined {\n const message = error.message;\n\n // Try specific HTTP status patterns first\n const patterns = [/\\bstatus[:\\s]+(\\d{3})\\b/i,\n // \"status: 404\" or \"status 404\"\n /\\bHTTP[\\/\\s]+\\d+(?:\\.\\d+)?\\s+(\\d{3})/i,\n // \"HTTP/1.1 404\" or \"HTTP 404\"\n /\\b(\\d{3})\\s+(?:error|failed|forbidden|unauthorized|not\\s+found)/i,\n // \"404 not found\"\n /\\berror[:\\s]+(\\d{3})\\b/i // \"error: 500\" or \"error 500\"\n ];\n for (const pattern of patterns) {\n const match = message.match(pattern);\n if (match) {\n return match[1];\n }\n }\n\n // Fallback: check for Supabase/PostgREST error structure\n if ('status' in (error as any) && typeof (error as any).status === 'number') {\n return String((error as any).status);\n }\n return undefined;\n }\n private async _markUploadSynced(record: PolAttachmentRecord): Promise<void> {\n await this.powersync.execute(`UPDATE ${this.table}\n SET state = ?, local_uri = ?, upload_error = NULL, upload_error_code = NULL\n WHERE id = ?`, [AttachmentState.SYNCED, record.upload_source_uri, record.id]);\n this.polLogger.info(`[PolAttachmentQueue] Upload complete: ${record.id}`);\n\n // Call completion callback\n if (this.options.onUploadComplete) {\n try {\n await this.options.onUploadComplete(record);\n } catch (error) {\n this.polLogger.error(`[PolAttachmentQueue] onUploadComplete failed for ${record.id}:`, error);\n }\n }\n }\n private async _markUploadPermanentFailure(record: PolAttachmentRecord, errorMessage: string, errorCode?: string): Promise<void> {\n await this.powersync.execute(`UPDATE ${this.table}\n SET state = ?, upload_error = ?, upload_error_code = ?\n WHERE id = ?`, [PolAttachmentState.FAILED_PERMANENT, errorMessage, errorCode || null, record.id]);\n this.polLogger.warn(`[PolAttachmentQueue] Permanent failure: ${record.id} - ${errorMessage}`);\n if (this.options.onUploadFailed) {\n this.options.onUploadFailed(record, new Error(errorMessage));\n }\n }\n private async _scheduleUploadRetry(record: PolAttachmentRecord, error: Error): Promise<void> {\n const retryCount = (record.upload_retry_count || 0) + 1;\n\n // Warning 4: Check if max retry count exceeded\n if (retryCount > this.uploadConfig.maxRetryCount) {\n this.polLogger.warn(`[PolAttachmentQueue] Max retry count (${this.uploadConfig.maxRetryCount}) exceeded for ${record.id}`);\n await this._markUploadPermanentFailure(record, `Max retry count exceeded after ${retryCount - 1} attempts. Last error: ${error.message}`, 'MAX_RETRIES');\n return;\n }\n\n // Calculate next retry with exponential backoff and jitter\n const baseDelay = calculateBackoffDelay(retryCount - 1, {\n baseDelayMs: this.uploadConfig.baseRetryDelayMs,\n maxDelayMs: this.uploadConfig.maxRetryDelayMs,\n backoffMultiplier: 2\n });\n const nextRetryAt = Date.now() + addJitter(baseDelay);\n await this.powersync.execute(`UPDATE ${this.table}\n SET upload_retry_count = ?, upload_next_retry_at = ?, upload_error = ?\n WHERE id = ?`, [retryCount, nextRetryAt, error.message, record.id]);\n const delaySeconds = Math.round((nextRetryAt - Date.now()) / 1000);\n this.polLogger.info(`[PolAttachmentQueue] Scheduled retry ${retryCount}/${this.uploadConfig.maxRetryCount} for ${record.id} in ${delaySeconds}s`);\n }\n\n // ─── Helpers ───────────────────────────────────────────────────────────────\n\n private _startPolling(query: string, onUpdate: (ids: string[]) => void): void {\n const poll = async () => {\n if (this._disposed) return;\n try {\n const result = await this.powersync.getAll<{\n id: string;\n }>(query);\n const ids = result?.map(r => r.id) ?? [];\n onUpdate(ids);\n } catch (error) {\n const errorMessage = String(error);\n if (!errorMessage.includes('no such table') && !errorMessage.includes('SQLITE_EMPTY')) {\n this.polLogger.warn('[PolAttachmentQueue] Poll error:', error);\n }\n }\n };\n\n // Initial poll\n poll();\n\n // Set up interval\n this._watchInterval = setInterval(poll, WATCH_INTERVAL_MS);\n }\n private async _migrateUploadColumns(): Promise<void> {\n // The attachment table is created by AbstractAttachmentQueue.init() as a REAL table\n // (not a view), so we can safely ALTER it to add upload-specific columns.\n // Note: Do NOT add AttachmentTable to your PowerSync Schema - that creates a view.\n const tableInfo = await this.powersync.getAll<{\n name: string;\n }>(`PRAGMA table_info(${this.table})`);\n const existingColumns = new Set(tableInfo.map(r => r.name));\n const uploadColumns = [{\n name: 'upload_source_uri',\n type: 'TEXT'\n }, {\n name: 'upload_error',\n type: 'TEXT'\n }, {\n name: 'upload_error_code',\n type: 'TEXT'\n }, {\n name: 'upload_retry_count',\n type: 'INTEGER DEFAULT 0'\n }, {\n name: 'upload_next_retry_at',\n type: 'INTEGER'\n }, {\n name: 'upload_metadata',\n type: 'TEXT'\n }, {\n name: 'upload_bucket_id',\n type: 'TEXT'\n }];\n for (const col of uploadColumns) {\n if (!existingColumns.has(col.name)) {\n this.polLogger.info(`[PolAttachmentQueue] Adding column: ${col.name}`);\n await this.powersync.execute(`ALTER TABLE ${this.table} ADD COLUMN ${col.name} ${col.type}`);\n }\n }\n }\n private async _copyToManagedCache(sourceUri: string, storagePath: string): Promise<string> {\n // Sanitize path for local filename\n const localPath = storagePath.replace(/[^a-zA-Z0-9._-]/g, '_');\n const cacheDir = this.platform.fileSystem.getCacheDirectory();\n const targetUri = `${cacheDir}upload_queue/${localPath}`;\n\n // Ensure directory exists\n const dir = targetUri.substring(0, targetUri.lastIndexOf('/'));\n await this.platform.fileSystem.makeDirectory(dir, {\n intermediates: true\n });\n\n // Copy file\n await this.platform.fileSystem.copyFile(sourceUri, targetUri);\n this.polLogger.info(`[PolAttachmentQueue] Copied to managed cache: ${targetUri}`);\n return targetUri;\n }\n private _getStatus(pendingCount: number): AttachmentSyncStatus {\n if (this.uploading || this.downloading || this._uploadProcessing) return 'syncing';\n if (this._uploadPaused) return 'paused';\n // Note: cache_full detection would require tracking cache size\n return 'complete';\n }\n private _notify(forceImmediate = false): void {\n if (this._progressCallbacks.size === 0) return;\n const now = Date.now();\n const timeSinceLastNotify = now - this._lastNotifyTime;\n if (this._notifyTimer) {\n clearTimeout(this._notifyTimer);\n this._notifyTimer = null;\n }\n const notifyAll = (stats: AttachmentSyncStats) => {\n for (const cb of this._progressCallbacks) {\n try {\n cb(stats);\n } catch (err) {\n this.polLogger.warn('[PolAttachmentQueue] Callback error:', err);\n }\n }\n };\n if (forceImmediate || timeSinceLastNotify >= NOTIFY_THROTTLE_MS) {\n this._lastNotifyTime = now;\n // Additional Issue: Log errors instead of swallowing them silently\n this.getStats().then(notifyAll).catch(err => {\n this.polLogger.warn('[PolAttachmentQueue] Stats error:', err);\n });\n } else {\n const delay = NOTIFY_THROTTLE_MS - timeSinceLastNotify;\n this._notifyTimer = setTimeout(() => {\n this._notifyTimer = null;\n this._lastNotifyTime = Date.now();\n // Additional Issue: Log errors instead of swallowing them silently\n this.getStats().then(notifyAll).catch(err => {\n this.polLogger.warn('[PolAttachmentQueue] Stats error:', err);\n });\n }, delay);\n }\n }\n private _sleep(ms: number): Promise<void> {\n return new Promise(resolve => {\n const timer = setTimeout(resolve, ms);\n this._uploadAbort.signal.addEventListener('abort', () => {\n clearTimeout(timer);\n resolve();\n }, {\n once: true\n });\n });\n }\n\n /**\n * Warning 6: Enhanced timeout that supports AbortSignal for actual cancellation.\n * When signal is provided and aborts, the promise rejects with an AbortError.\n */\n private _withTimeout<T>(promise: Promise<T>, ms: number, message: string, signal?: AbortSignal): Promise<T> {\n return new Promise((resolve, reject) => {\n // Handle pre-aborted signal\n if (signal?.aborted) {\n reject(new DOMException('Operation aborted', 'AbortError'));\n return;\n }\n const timer = setTimeout(() => {\n reject(new Error(message));\n }, ms);\n\n // Listen for abort signal\n const abortHandler = () => {\n clearTimeout(timer);\n reject(new DOMException('Operation aborted', 'AbortError'));\n };\n if (signal) {\n signal.addEventListener('abort', abortHandler, {\n once: true\n });\n }\n promise.then(result => {\n clearTimeout(timer);\n if (signal) {\n signal.removeEventListener('abort', abortHandler);\n }\n resolve(result);\n }, err => {\n clearTimeout(timer);\n if (signal) {\n signal.removeEventListener('abort', abortHandler);\n }\n reject(err);\n });\n });\n }\n}\n\n// ─── Factory Function ────────────────────────────────────────────────────────\n\n/**\n * Create a PolAttachmentQueue from a simplified config.\n *\n * This is a convenience factory that converts from the user-friendly\n * PolAttachmentQueueConfig to the options required by PolAttachmentQueue.\n */\nexport function createPolAttachmentQueue(powersync: PowerSyncDBInterface, platform: PlatformAdapter, config: PolAttachmentQueueConfig): PolAttachmentQueue {\n // Note: PolAttachmentQueue constructor creates its own PolStorageAdapter internally,\n // so we don't need to pass one here. The constructor will use attachmentDirectoryName\n // from the options to create the storage adapter.\n return new PolAttachmentQueue({\n powersync,\n // storage is created internally by the constructor using platform + remoteStorage + attachmentDirectoryName\n storage: undefined as never,\n // Will be overridden by constructor\n platform,\n remoteStorage: config.remoteStorage,\n source: config.source,\n attachmentDirectoryName: config.attachmentTableName ?? 'attachments',\n attachmentTableName: config.attachmentTableName ?? 'attachments',\n performInitialSync: config.performInitialSync,\n downloadAttachments: true,\n onDownloadError: config.onDownloadError ? async (attachment, error) => {\n const result = await config.onDownloadError!(attachment as PolAttachmentRecord, error);\n return result;\n } : undefined,\n uploadHandler: config.upload?.handler,\n uploadConfig: config.upload?.config,\n onUploadComplete: config.onUploadComplete,\n onUploadFailed: config.onUploadFailed,\n compression: config.compression,\n cache: config.cache\n });\n}"],"mappings":"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","names":["EncodingType","PolAttachmentState","AbstractAttachmentQueue","AttachmentState","AbstractAttachmentQueue","AttachmentState","uri"]}
|