@pol-studios/powersync 1.0.6 → 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.
Files changed (128) hide show
  1. package/README.md +933 -0
  2. package/dist/CacheSettingsManager-uz-kbnRH.d.ts +461 -0
  3. package/dist/attachments/index.d.ts +745 -332
  4. package/dist/attachments/index.js +152 -6
  5. package/dist/{types-Cd7RhNqf.d.ts → background-sync-ChCXW-EV.d.ts} +53 -2
  6. package/dist/chunk-24RDMMCL.js +44 -0
  7. package/dist/chunk-24RDMMCL.js.map +1 -0
  8. package/dist/chunk-4TXTAEF2.js +2060 -0
  9. package/dist/chunk-4TXTAEF2.js.map +1 -0
  10. package/dist/chunk-63PXSPIN.js +358 -0
  11. package/dist/chunk-63PXSPIN.js.map +1 -0
  12. package/dist/chunk-654ERHA7.js +1 -0
  13. package/dist/chunk-A4IBBWGO.js +377 -0
  14. package/dist/chunk-A4IBBWGO.js.map +1 -0
  15. package/dist/chunk-BRXQNASY.js +1720 -0
  16. package/dist/chunk-BRXQNASY.js.map +1 -0
  17. package/dist/chunk-CAB26E6F.js +142 -0
  18. package/dist/chunk-CAB26E6F.js.map +1 -0
  19. package/dist/{chunk-EJ23MXPQ.js → chunk-CGL33PL4.js} +3 -1
  20. package/dist/chunk-CGL33PL4.js.map +1 -0
  21. package/dist/{chunk-R4YFWQ3Q.js → chunk-CUCAYK7Z.js} +309 -92
  22. package/dist/chunk-CUCAYK7Z.js.map +1 -0
  23. package/dist/chunk-FV2HXEIY.js +124 -0
  24. package/dist/chunk-FV2HXEIY.js.map +1 -0
  25. package/dist/chunk-HWSNV45P.js +279 -0
  26. package/dist/chunk-HWSNV45P.js.map +1 -0
  27. package/dist/{chunk-62J2DPKX.js → chunk-KN2IZERF.js} +530 -413
  28. package/dist/chunk-KN2IZERF.js.map +1 -0
  29. package/dist/{chunk-7EMDVIZX.js → chunk-N75DEF5J.js} +19 -1
  30. package/dist/chunk-N75DEF5J.js.map +1 -0
  31. package/dist/chunk-P4HZA6ZT.js +83 -0
  32. package/dist/chunk-P4HZA6ZT.js.map +1 -0
  33. package/dist/chunk-P6WOZO7H.js +49 -0
  34. package/dist/chunk-P6WOZO7H.js.map +1 -0
  35. package/dist/chunk-T4AO7JIG.js +1 -0
  36. package/dist/chunk-TGBT5XBE.js +1 -0
  37. package/dist/{chunk-FPTDATY5.js → chunk-VACPAAQZ.js} +54 -12
  38. package/dist/chunk-VACPAAQZ.js.map +1 -0
  39. package/dist/chunk-WGHNIAF7.js +329 -0
  40. package/dist/chunk-WGHNIAF7.js.map +1 -0
  41. package/dist/{chunk-3AYXHQ4W.js → chunk-WN5ZJ3E2.js} +108 -47
  42. package/dist/chunk-WN5ZJ3E2.js.map +1 -0
  43. package/dist/chunk-XAEII4ZX.js +456 -0
  44. package/dist/chunk-XAEII4ZX.js.map +1 -0
  45. package/dist/chunk-XOY2CJ67.js +289 -0
  46. package/dist/chunk-XOY2CJ67.js.map +1 -0
  47. package/dist/chunk-YHTZ7VMV.js +1 -0
  48. package/dist/chunk-YSTEESEG.js +676 -0
  49. package/dist/chunk-YSTEESEG.js.map +1 -0
  50. package/dist/chunk-Z6VOBGTU.js +32 -0
  51. package/dist/chunk-Z6VOBGTU.js.map +1 -0
  52. package/dist/chunk-ZM4ENYMF.js +230 -0
  53. package/dist/chunk-ZM4ENYMF.js.map +1 -0
  54. package/dist/connector/index.d.ts +236 -4
  55. package/dist/connector/index.js +15 -4
  56. package/dist/core/index.d.ts +16 -3
  57. package/dist/core/index.js +6 -2
  58. package/dist/error/index.d.ts +54 -0
  59. package/dist/error/index.js +7 -0
  60. package/dist/error/index.js.map +1 -0
  61. package/dist/index.d.ts +102 -12
  62. package/dist/index.js +309 -37
  63. package/dist/index.native.d.ts +22 -10
  64. package/dist/index.native.js +309 -38
  65. package/dist/index.web.d.ts +22 -10
  66. package/dist/index.web.js +310 -38
  67. package/dist/maintenance/index.d.ts +118 -0
  68. package/dist/maintenance/index.js +16 -0
  69. package/dist/maintenance/index.js.map +1 -0
  70. package/dist/platform/index.d.ts +16 -1
  71. package/dist/platform/index.js.map +1 -1
  72. package/dist/platform/index.native.d.ts +2 -2
  73. package/dist/platform/index.native.js +1 -1
  74. package/dist/platform/index.web.d.ts +1 -1
  75. package/dist/platform/index.web.js +1 -1
  76. package/dist/pol-attachment-queue-BVAIueoP.d.ts +817 -0
  77. package/dist/provider/index.d.ts +451 -21
  78. package/dist/provider/index.js +32 -13
  79. package/dist/react/index.d.ts +372 -0
  80. package/dist/react/index.js +25 -0
  81. package/dist/react/index.js.map +1 -0
  82. package/dist/storage/index.d.ts +6 -0
  83. package/dist/storage/index.js +42 -0
  84. package/dist/storage/index.js.map +1 -0
  85. package/dist/storage/index.native.d.ts +6 -0
  86. package/dist/storage/index.native.js +40 -0
  87. package/dist/storage/index.native.js.map +1 -0
  88. package/dist/storage/index.web.d.ts +6 -0
  89. package/dist/storage/index.web.js +40 -0
  90. package/dist/storage/index.web.js.map +1 -0
  91. package/dist/storage/upload/index.d.ts +54 -0
  92. package/dist/storage/upload/index.js +15 -0
  93. package/dist/storage/upload/index.js.map +1 -0
  94. package/dist/storage/upload/index.native.d.ts +56 -0
  95. package/dist/storage/upload/index.native.js +15 -0
  96. package/dist/storage/upload/index.native.js.map +1 -0
  97. package/dist/storage/upload/index.web.d.ts +2 -0
  98. package/dist/storage/upload/index.web.js +14 -0
  99. package/dist/storage/upload/index.web.js.map +1 -0
  100. package/dist/supabase-connector-T9vHq_3i.d.ts +202 -0
  101. package/dist/sync/index.d.ts +288 -23
  102. package/dist/sync/index.js +22 -10
  103. package/dist/{index-l3iL9Jte.d.ts → types-B212hgfA.d.ts} +101 -158
  104. package/dist/{types-afHtE1U_.d.ts → types-CDqWh56B.d.ts} +2 -0
  105. package/dist/types-CyvBaAl8.d.ts +60 -0
  106. package/dist/types-D0WcHrq6.d.ts +234 -0
  107. package/package.json +89 -5
  108. package/dist/chunk-32OLICZO.js +0 -1
  109. package/dist/chunk-3AYXHQ4W.js.map +0 -1
  110. package/dist/chunk-5FIMA26D.js +0 -1
  111. package/dist/chunk-62J2DPKX.js.map +0 -1
  112. package/dist/chunk-7EMDVIZX.js.map +0 -1
  113. package/dist/chunk-EJ23MXPQ.js.map +0 -1
  114. package/dist/chunk-FPTDATY5.js.map +0 -1
  115. package/dist/chunk-KCDG2MNP.js +0 -1431
  116. package/dist/chunk-KCDG2MNP.js.map +0 -1
  117. package/dist/chunk-OLHGI472.js +0 -1
  118. package/dist/chunk-PAFBKNL3.js +0 -99
  119. package/dist/chunk-PAFBKNL3.js.map +0 -1
  120. package/dist/chunk-R4YFWQ3Q.js.map +0 -1
  121. package/dist/chunk-V6LJ6MR2.js +0 -740
  122. package/dist/chunk-V6LJ6MR2.js.map +0 -1
  123. package/dist/chunk-VJCL2SWD.js +0 -1
  124. package/dist/failed-upload-store-C0cLxxPz.d.ts +0 -33
  125. /package/dist/{chunk-32OLICZO.js.map → chunk-654ERHA7.js.map} +0 -0
  126. /package/dist/{chunk-5FIMA26D.js.map → chunk-T4AO7JIG.js.map} +0 -0
  127. /package/dist/{chunk-OLHGI472.js.map → chunk-TGBT5XBE.js.map} +0 -0
  128. /package/dist/{chunk-VJCL2SWD.js.map → chunk-YHTZ7VMV.js.map} +0 -0
@@ -1,399 +1,812 @@
1
- import { A as AbstractPowerSyncDatabase } from '../types-afHtE1U_.js';
2
- import { PlatformAdapter } from '../platform/index.js';
1
+ import { f as AttachmentStorageAdapter, d as PolAttachmentRecord, h as UploadHandler, U as UploadConfig, t as UploadStatus, k as CacheConfig, W as WatchConfig } from '../pol-attachment-queue-BVAIueoP.js';
2
+ export { e as AttachmentConfig, z as AttachmentRecord, A as AttachmentSourceConfig, w as AttachmentStatsRow, v as AttachmentSyncStats, u as AttachmentSyncStatus, B as BatchFilterContext, m as CACHE_SIZE_PRESETS, x as CacheFileRow, n as CacheSizePreset, o as CacheSizeValue, y as CachedSizeRow, C as CompressionConfig, l as DEFAULT_CACHE_CONFIG, D as DEFAULT_COMPRESSION_CONFIG, j as DEFAULT_DOWNLOAD_CONFIG, g as DEFAULT_UPLOAD_CONFIG, i as DownloadConfig, q as DownloadPhase, r as DownloadStatus, E as EvictRow, I as IdRow, P as PolAttachmentQueue, a as PolAttachmentQueueOptions, b as PolAttachmentState, S as SkipDownloadContext, s as UploadPhase, c as createPolAttachmentQueue, p as formatCacheSize } from '../pol-attachment-queue-BVAIueoP.js';
3
+ import { StorageAdapter, EncodingType, AttachmentRecord } from '@powersync/attachments';
4
+ export { ATTACHMENT_TABLE, AbstractAttachmentQueue, AttachmentState, AttachmentTable, AttachmentTableOptions, AttachmentQueueOptions as BaseAttachmentQueueOptions, DEFAULT_ATTACHMENT_QUEUE_OPTIONS, EncodingType, AttachmentRecord as OfficialAttachmentRecord, StorageAdapter } from '@powersync/attachments';
5
+ import { PlatformAdapter, LoggerAdapter } from '../platform/index.js';
6
+ import { AbstractPowerSyncDatabase } from '@powersync/common';
7
+ export { AbstractPowerSyncDatabase as PowerSyncDBInterface } from '@powersync/common';
8
+ import '../types-CDqWh56B.js';
3
9
 
4
10
  /**
5
- * Attachment Queue Types for @pol-studios/powersync
11
+ * POL Storage Adapter
6
12
  *
7
- * Defines interfaces for the attachment queue system that handles
8
- * offline file caching with download, compression, and eviction.
9
- */
10
- /**
11
- * State of an attachment in the queue.
12
- * NOTE: These do NOT match @powersync/attachments enum ordering (values 0-2 differ).
13
- */
14
- declare enum AttachmentState {
15
- /** Waiting to be downloaded */
16
- QUEUED_DOWNLOAD = 0,
17
- /** Waiting for initial sync */
18
- QUEUED_SYNC = 1,
19
- /** Waiting to be uploaded */
20
- QUEUED_UPLOAD = 2,
21
- /** Fully synced (downloaded or uploaded) */
22
- SYNCED = 3,
23
- /** Archived (removed from sync but record kept) */
24
- ARCHIVED = 4
25
- }
26
- /**
27
- * Record representing an attachment in the queue database.
28
- */
29
- interface AttachmentRecord {
30
- /** Unique identifier (typically storage path) */
31
- id: string;
32
- /** Filename for display and type inference */
33
- filename: string;
34
- /** MIME type of the file */
35
- media_type: string;
36
- /** Current state in the queue */
37
- state: AttachmentState;
38
- /** Local file URI (set after download) */
39
- local_uri?: string | null;
40
- /** File size in bytes */
41
- size?: number;
42
- /** Timestamp when the attachment was created */
43
- timestamp?: number;
44
- }
45
- /**
46
- * Configuration for the source table that contains attachment references.
47
- * This makes the attachment queue reusable across different tables/projects.
13
+ * Implements the official @powersync/attachments StorageAdapter interface,
14
+ * bridging our PlatformAdapter file system and remote storage adapters.
15
+ *
16
+ * This adapter combines:
17
+ * - Local file operations via PlatformAdapter.fileSystem
18
+ * - Remote upload/download via AttachmentStorageAdapter (e.g., Supabase)
48
19
  */
49
- interface AttachmentSourceConfig {
20
+
21
+ interface PolStorageAdapterOptions {
50
22
  /**
51
- * Table name containing attachment references.
52
- * @example "EquipmentUnitMediaContent"
23
+ * Platform adapter for local file system operations.
53
24
  */
54
- table: string;
25
+ platform: PlatformAdapter;
55
26
  /**
56
- * Column containing the storage path / attachment ID.
57
- * @example "storagePath"
27
+ * Remote storage adapter for upload/download operations.
58
28
  */
59
- idColumn: string;
29
+ remoteStorage: AttachmentStorageAdapter;
60
30
  /**
61
- * Column to order by for "newest first" downloads.
62
- * Set to null to skip ordering.
63
- * @example "takenOn"
31
+ * Base directory name for attachments within the cache directory.
32
+ * Defaults to 'attachments'.
64
33
  */
65
- orderByColumn: string | null;
34
+ attachmentDirectoryName?: string;
66
35
  /**
67
- * Optional filter config to exclude attachments from archived/unsynced projects.
68
- * When set, the attachment query JOINs through intermediary tables to ensure
69
- * only attachments belonging to synced projects are downloaded.
36
+ * Optional logger for diagnostic messages.
37
+ * If provided, will log warnings when deprecated code paths are hit.
70
38
  */
71
- projectFilter?: {
72
- /** Foreign key column in the source table (e.g., "equipmentUnitId") */
73
- foreignKey: string;
74
- /** Intermediary table to JOIN through (e.g., "EquipmentFixtureUnit") */
75
- intermediaryTable: string;
76
- /** Column in intermediary table that links to ProjectDatabase (e.g., "projectDatabaseId") */
77
- projectForeignKey: string;
39
+ logger?: {
40
+ warn(message: string, ...args: unknown[]): void;
78
41
  };
79
42
  }
80
43
  /**
81
- * Interface for attachment storage operations (e.g., Supabase Storage).
44
+ * Storage adapter that implements the official @powersync/attachments interface.
45
+ *
46
+ * Combines local file system operations (via PlatformAdapter) with remote
47
+ * storage operations (via AttachmentStorageAdapter like Supabase).
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * const storageAdapter = new PolStorageAdapter({
52
+ * platform: platformAdapter,
53
+ * remoteStorage: supabaseStorageAdapter,
54
+ * });
55
+ * ```
82
56
  */
83
- interface AttachmentStorageAdapter {
57
+ declare class PolStorageAdapter implements StorageAdapter {
58
+ private readonly platform;
59
+ private readonly remoteStorage;
60
+ private readonly attachmentDirectoryName;
61
+ private readonly userStorageDirectory;
62
+ private readonly logger?;
63
+ constructor(options: PolStorageAdapterOptions);
64
+ /**
65
+ * Upload a file to remote storage.
66
+ */
67
+ uploadFile(filePath: string, data: ArrayBuffer, options?: {
68
+ mediaType?: string;
69
+ }): Promise<void>;
84
70
  /**
85
71
  * Download a file from remote storage.
86
- * @param filePath - The storage path of the file
87
- * @returns The file data as a Blob or base64 string
72
+ *
73
+ * NOTE: This method implements the official @powersync/attachments StorageAdapter
74
+ * interface which requires returning Promise<Blob>. For the memory-optimized
75
+ * file path passthrough, PolAttachmentQueue.downloadRecord() calls
76
+ * remoteStorage.downloadFile() directly to get the file:// path.
77
+ *
78
+ * This method handles base64 string → Blob conversion for backward compatibility.
79
+ */
80
+ downloadFile(filePath: string): Promise<Blob>;
81
+ /**
82
+ * Write data to a local file.
83
+ */
84
+ writeFile(fileUri: string, base64Data: string, options?: {
85
+ encoding?: EncodingType;
86
+ }): Promise<void>;
87
+ /**
88
+ * Read a local file's contents.
89
+ */
90
+ readFile(fileUri: string, options?: {
91
+ encoding?: EncodingType;
92
+ mediaType?: string;
93
+ }): Promise<ArrayBuffer>;
94
+ /**
95
+ * Delete a local file.
96
+ */
97
+ deleteFile(uri: string, _options?: {
98
+ filename?: string;
99
+ }): Promise<void>;
100
+ /**
101
+ * Check if a local file exists.
102
+ */
103
+ fileExists(fileUri: string): Promise<boolean>;
104
+ /**
105
+ * Create a directory (with intermediate directories).
88
106
  */
89
- downloadFile(filePath: string): Promise<Blob | string>;
107
+ makeDir(uri: string): Promise<void>;
90
108
  /**
91
- * Upload a file to remote storage (optional - not all queues need upload).
92
- * @param filePath - The storage path to upload to
93
- * @param data - The file data to upload
109
+ * Copy a file from source to destination.
94
110
  */
95
- uploadFile?(filePath: string, data: Blob | string): Promise<void>;
111
+ copyFile(sourceUri: string, targetUri: string): Promise<void>;
96
112
  /**
97
- * Delete a file from remote storage (optional).
98
- * @param filePath - The storage path of the file
113
+ * Get the user's storage directory for attachments.
114
+ * Returns the cache directory path ending with '/'.
99
115
  */
100
- deleteFile?(filePath: string): Promise<void>;
116
+ getUserStorageDirectory(): string;
101
117
  /**
102
- * Resolve the storage bucket for a file path.
103
- * Allows routing different files to different buckets.
104
- * @param filePath - The file path to resolve
105
- * @returns The bucket name
118
+ * Warning 8: Optimized base64 encoding using batch processing.
119
+ * Processes in chunks of 1024 bytes for better performance with large files.
106
120
  */
107
- resolveBucket?(filePath: string): string;
121
+ private _arrayBufferToBase64;
122
+ private _base64ToArrayBuffer;
108
123
  }
124
+
109
125
  /**
110
- * Configuration for image compression.
111
- */
112
- interface CompressionConfig {
113
- /** Enable compression (default: true) */
114
- enabled: boolean;
115
- /** Compression quality 0.0-1.0 (default: 0.7) */
116
- quality: number;
117
- /** Max width before resizing (default: 2048) */
118
- maxWidth: number;
119
- /** Skip files under this size in bytes (default: 100KB) */
120
- skipSizeBytes: number;
121
- /** Skip if already under this size in bytes (default: 300KB) */
122
- targetSizeBytes: number;
123
- }
126
+ * Attachment State Machine
127
+ *
128
+ * Defines state transitions, protected states, and validation logic
129
+ * for the POL attachment queue system.
130
+ *
131
+ * @module attachments/state-machine
132
+ */
124
133
  /**
125
- * Default compression configuration.
134
+ * States that represent active upload workflows.
135
+ * Records in these states should NEVER be demoted to download states.
136
+ *
137
+ * This fixes a race condition in the parent AbstractAttachmentQueue.watchAttachmentIds():
138
+ * - Upload records have `local_uri = null` (they use `upload_source_uri` instead)
139
+ * - When the CRUD record syncs back, `onAttachmentIdsChange` fires
140
+ * - Parent checks `record.local_uri == null` → true for uploads
141
+ * - Parent changes state from QUEUED_UPLOAD to QUEUED_DOWNLOAD
142
+ * - Upload is abandoned, download fails (file doesn't exist in remote storage yet)
143
+ *
144
+ * By protecting these states, we prevent the parent's logic from interfering
145
+ * with in-progress or queued uploads.
146
+ *
147
+ * @see https://github.com/powersync-ja/powersync-js/blob/main/packages/attachments/src/AbstractAttachmentQueue.ts
126
148
  */
127
- declare const DEFAULT_COMPRESSION_CONFIG: CompressionConfig;
149
+ declare const PROTECTED_UPLOAD_STATES: Set<number>;
128
150
  /**
129
- * Configuration for the download engine.
151
+ * States that indicate a download is pending or in progress.
130
152
  */
131
- interface DownloadConfig {
132
- /** Maximum concurrent downloads (default: 50) */
133
- concurrency: number;
134
- /** Download timeout per file in ms (default: 60000) */
135
- timeoutMs: number;
136
- /** Retry delay between batches in ms (default: 5000) */
137
- retryDelayMs: number;
138
- }
153
+ declare const PENDING_DOWNLOAD_STATES: Set<number>;
139
154
  /**
140
- * Default download configuration.
155
+ * States that indicate the attachment is locally available.
141
156
  */
142
- declare const DEFAULT_DOWNLOAD_CONFIG: DownloadConfig;
157
+ declare const LOCALLY_AVAILABLE_STATES: Set<number>;
143
158
  /**
144
- * Configuration for cache management.
159
+ * Check if a record is in a protected upload state.
160
+ * Protected states should not be demoted to download states.
145
161
  */
146
- interface CacheConfig {
147
- /** Maximum cache size in bytes (default: 5GB) */
148
- maxSize: number;
149
- /** Stop downloads at this percentage of max (default: 0.95 = 95%) */
150
- downloadStopThreshold: number;
151
- /** Trigger eviction at this percentage (default: 1.0 = 100%) */
152
- evictionTriggerThreshold: number;
153
- }
162
+ declare function isProtectedUploadState(state: number): boolean;
154
163
  /**
155
- * Default cache configuration.
164
+ * Check if a record is in a pending download state.
156
165
  */
157
- declare const DEFAULT_CACHE_CONFIG: CacheConfig;
166
+ declare function isPendingDownloadState(state: number): boolean;
158
167
  /**
159
- * Full configuration for the attachment queue.
168
+ * Check if a record has a locally available file.
160
169
  */
161
- interface AttachmentQueueConfig {
162
- /** Source table configuration */
163
- source: AttachmentSourceConfig;
164
- /** Storage adapter for downloading files */
165
- storage: AttachmentStorageAdapter;
166
- /** Table name for storing attachment records (default: "photo_attachments") */
167
- attachmentTableName?: string;
168
- /** Perform initial sync on initialization (default: true) */
169
- performInitialSync?: boolean;
170
- /** Download configuration */
171
- download?: Partial<DownloadConfig>;
172
- /** Cache configuration */
173
- cache?: Partial<CacheConfig>;
174
- /** Compression configuration */
175
- compression?: Partial<CompressionConfig>;
176
- /**
177
- * Called when a download fails.
178
- * Return { retry: true } to retry, { retry: false } to skip.
179
- */
180
- onDownloadError?: (attachment: AttachmentRecord, error: Error) => Promise<{
181
- retry: boolean;
170
+ declare function isLocallyAvailable(state: number): boolean;
171
+ /**
172
+ * Check if a state transition is allowed.
173
+ * Prevents protected upload states from being demoted to download states.
174
+ *
175
+ * @param currentState - The current state of the record
176
+ * @param newState - The proposed new state
177
+ * @returns true if the transition is allowed
178
+ */
179
+ declare function isStateTransitionAllowed(currentState: number, newState: number): boolean;
180
+ /**
181
+ * Get the appropriate state for a new attachment based on context.
182
+ *
183
+ * @param hasLocalUri - Whether the attachment has a local URI
184
+ * @param hasUploadSourceUri - Whether the attachment has an upload source URI
185
+ * @param currentState - The current state (if any)
186
+ * @returns The appropriate state for the attachment
187
+ */
188
+ declare function determineAttachmentState(hasLocalUri: boolean, hasUploadSourceUri: boolean, currentState?: number): number;
189
+ /**
190
+ * Validate that a string is a safe SQL identifier.
191
+ * Prevents SQL injection via config values.
192
+ *
193
+ * @param value - The value to validate
194
+ * @param name - The name of the parameter (for error messages)
195
+ * @throws Error if the value is not a valid SQL identifier
196
+ */
197
+ declare function validateSqlIdentifier$1(value: string, name: string): void;
198
+ /**
199
+ * Build a SQL IN clause with protected upload states excluded.
200
+ * Used for queries that should not affect upload records.
201
+ */
202
+ declare function getProtectedStatesInClause(): string;
203
+ /**
204
+ * Build a SQL condition that excludes protected upload states.
205
+ */
206
+ declare function getExcludeProtectedStatesCondition(stateColumn?: string): string;
207
+
208
+ /**
209
+ * Download Manager
210
+ *
211
+ * Handles download-related logic including batch filtering,
212
+ * download filter application, and download record processing.
213
+ *
214
+ * @module attachments/download-manager
215
+ */
216
+
217
+ /**
218
+ * Dependencies required by the download manager.
219
+ */
220
+ interface DownloadManagerDeps {
221
+ /** PowerSync database instance */
222
+ powersync: AbstractPowerSyncDatabase;
223
+ /** Platform adapter for file system operations */
224
+ platform: PlatformAdapter;
225
+ /** Logger adapter */
226
+ logger: LoggerAdapter;
227
+ /** Remote storage adapter for downloading files */
228
+ remoteStorage: AttachmentStorageAdapter;
229
+ /** Storage adapter with file operations */
230
+ storage: {
231
+ fileExists(uri: string): Promise<boolean>;
232
+ writeFile(uri: string, data: string, options?: {
233
+ encoding?: EncodingType;
234
+ }): Promise<void>;
235
+ makeDir(uri: string): Promise<void>;
236
+ copyFile(src: string, dest: string): Promise<void>;
237
+ };
238
+ /** Attachment table name */
239
+ tableName: string;
240
+ /** Function to get local URI from local path */
241
+ getLocalUri: (localPath: string) => string;
242
+ /** Function to get local file path suffix */
243
+ getLocalFilePathSuffix: (filename: string) => string;
244
+ /** Function to update a record */
245
+ update: (record: AttachmentRecord) => Promise<void>;
246
+ /** Whether downloads are enabled */
247
+ downloadAttachments?: boolean;
248
+ /** Callback when download errors occur */
249
+ onDownloadError?: (record: AttachmentRecord, error: Error) => Promise<{
250
+ retry?: boolean;
182
251
  }>;
183
- /**
184
- * Called when sync progress changes.
185
- */
186
- onProgress?: (stats: AttachmentSyncStats) => void;
252
+ /** Function to notify progress updates */
253
+ notify: (immediate: boolean) => void;
254
+ /** Function to invalidate stats cache */
255
+ invalidateStatsCache: () => void;
187
256
  }
188
257
  /**
189
- * Current phase of a download operation.
258
+ * Convert a Blob to ArrayBuffer with fallback for React Native.
259
+ *
260
+ * Blob.arrayBuffer() is a web API not available in React Native's Hermes engine.
261
+ * Falls back to FileReader.readAsArrayBuffer() which works better than readAsDataURL()
262
+ * in React Native (readAsDataURL hangs but readAsArrayBuffer typically works).
190
263
  */
191
- type DownloadPhase = 'downloading' | 'compressing' | 'complete' | 'error';
264
+ declare function blobToArrayBuffer(blob: Blob): Promise<ArrayBuffer>;
192
265
  /**
193
- * Status of an individual download.
266
+ * Download a single attachment record.
267
+ *
268
+ * Avoids FileReader.readAsDataURL() which hangs in React Native.
269
+ * The parent implementation uses FileReader to convert Blob to base64,
270
+ * but FileReader hangs indefinitely in React Native environments.
271
+ * This override writes the downloaded file directly to the file system
272
+ * using platform-native methods.
273
+ *
274
+ * Note: Filtering is handled by skipDownload callback in the queue.
194
275
  */
195
- interface DownloadStatus {
196
- /** Attachment ID */
197
- id: string;
198
- /** Filename being downloaded */
199
- filename: string;
200
- /** Current phase */
201
- phase: DownloadPhase;
202
- }
276
+ declare function downloadRecord(deps: DownloadManagerDeps, record: AttachmentRecord): Promise<boolean>;
277
+
278
+ /**
279
+ * Upload Manager
280
+ *
281
+ * Handles upload processing, retry scheduling, error handling,
282
+ * and upload lifecycle management.
283
+ *
284
+ * @module attachments/upload-manager
285
+ */
286
+
203
287
  /**
204
- * Why downloads are stopped (if not actively syncing).
205
- */
206
- type AttachmentSyncStatus = 'syncing' | 'paused' | 'cache_full' | 'complete';
207
- /**
208
- * Statistics about the attachment sync progress.
209
- */
210
- interface AttachmentSyncStats {
211
- /** Number of attachments that have been downloaded */
212
- syncedCount: number;
213
- /** Total size of synced attachments in bytes */
214
- syncedSize: number;
215
- /** Number of attachments waiting to be downloaded */
216
- pendingCount: number;
217
- /** Total expected attachments (synced + pending) */
218
- totalExpected: number;
219
- /** Maximum cache size in bytes */
220
- maxCacheSize: number;
221
- /** Current compression quality (0.1 to 1.0) */
222
- compressionQuality: number;
223
- /** Current sync status */
224
- status: AttachmentSyncStatus;
225
- /** Whether downloads are paused */
226
- isPaused: boolean;
227
- /** Whether currently processing downloads */
228
- isProcessing: boolean;
229
- /** Currently active downloads */
230
- activeDownloads: DownloadStatus[];
288
+ * Dependencies required by the upload manager.
289
+ */
290
+ interface UploadManagerDeps {
291
+ /** PowerSync database instance */
292
+ powersync: AbstractPowerSyncDatabase;
293
+ /** Platform adapter for file system operations */
294
+ platform: PlatformAdapter;
295
+ /** Logger adapter */
296
+ logger: LoggerAdapter;
297
+ /** Attachment table name */
298
+ tableName: string;
299
+ /** Upload handler for uploading files */
300
+ uploadHandler?: UploadHandler;
301
+ /** Upload configuration */
302
+ uploadConfig: UploadConfig;
303
+ /** Callback when upload completes */
304
+ onUploadComplete?: (record: PolAttachmentRecord) => Promise<void>;
305
+ /** Callback when upload fails permanently */
306
+ onUploadFailed?: (record: PolAttachmentRecord, error: Error) => void;
307
+ /** Function to notify progress updates */
308
+ notify: (immediate: boolean) => void;
231
309
  }
232
- /** Row from stats query */
233
- interface AttachmentStatsRow {
234
- state: number;
235
- cnt: number;
236
- sz: number;
310
+ /**
311
+ * State for the upload manager.
312
+ */
313
+ interface UploadManagerState {
314
+ /** Whether upload processing is active */
315
+ processing: boolean;
316
+ /** Whether uploads are paused */
317
+ paused: boolean;
318
+ /** Abort controller for cancellation */
319
+ abortController: AbortController;
320
+ /** Map of active uploads */
321
+ activeUploads: Map<string, UploadStatus>;
322
+ /** Set of active upload promises for cleanup during dispose */
323
+ activePromises: Set<Promise<void>>;
237
324
  }
238
- /** Row for cache file operations */
239
- interface CacheFileRow {
240
- id: string;
241
- local_uri: string;
325
+ /**
326
+ * Check if an error is permanent (should not retry).
327
+ */
328
+ declare function isPermanentError(error: Error): boolean;
329
+ /**
330
+ * Extract HTTP status code from error message.
331
+ * Uses specific patterns to avoid false positives from numbers in other contexts.
332
+ */
333
+ declare function extractErrorCode(error: Error): string | undefined;
334
+ /**
335
+ * Get pending uploads that need retry.
336
+ */
337
+ declare function getPendingUploads(powersync: AbstractPowerSyncDatabase, tableName: string): Promise<PolAttachmentRecord[]>;
338
+ /**
339
+ * Get the soonest retry time for all uploads currently in backoff.
340
+ * Returns null if there are no uploads waiting for retry.
341
+ */
342
+ declare function getSoonestRetryTime(powersync: AbstractPowerSyncDatabase, tableName: string): Promise<number | null>;
343
+ /**
344
+ * Get uploads that have permanently failed.
345
+ */
346
+ declare function getFailedPermanentUploads(powersync: AbstractPowerSyncDatabase, tableName: string): Promise<PolAttachmentRecord[]>;
347
+ /**
348
+ * Get stale uploads (failing for > staleDaysThreshold).
349
+ */
350
+ declare function getStaleUploads(powersync: AbstractPowerSyncDatabase, tableName: string, staleDaysThreshold: number): Promise<PolAttachmentRecord[]>;
351
+ /**
352
+ * Get SYNCED uploads that have pending onComplete callbacks.
353
+ */
354
+ declare function getSyncedUploadsWithPendingCallback(powersync: AbstractPowerSyncDatabase, tableName: string): Promise<PolAttachmentRecord[]>;
355
+ /**
356
+ * Clear the onCompleteCallback flag from a record's metadata.
357
+ */
358
+ declare function clearUploadCallback(powersync: AbstractPowerSyncDatabase, tableName: string, id: string, uploadMetadata?: string | null): Promise<void>;
359
+ /**
360
+ * Mark an upload as synced (complete).
361
+ */
362
+ declare function markUploadSynced(deps: UploadManagerDeps, record: PolAttachmentRecord): Promise<void>;
363
+ /**
364
+ * Mark an upload as permanently failed.
365
+ */
366
+ declare function markUploadPermanentFailure(deps: UploadManagerDeps, record: PolAttachmentRecord, errorMessage: string, errorCode?: string): Promise<void>;
367
+ /**
368
+ * Schedule an upload retry with exponential backoff.
369
+ */
370
+ declare function scheduleUploadRetry(deps: UploadManagerDeps, record: PolAttachmentRecord, error: Error): Promise<void>;
371
+ /**
372
+ * Upload a single record.
373
+ */
374
+ declare function uploadOne(deps: UploadManagerDeps, state: UploadManagerState, record: PolAttachmentRecord): Promise<void>;
375
+ /**
376
+ * Start the upload processing loop.
377
+ */
378
+ declare function startUploadProcessing(deps: UploadManagerDeps, state: UploadManagerState): Promise<void>;
379
+ /**
380
+ * Create initial upload manager state.
381
+ */
382
+ declare function createUploadManagerState(): UploadManagerState;
383
+ /**
384
+ * Create upload manager dependencies with defaults.
385
+ */
386
+ declare function createUploadManagerDeps(partial: Omit<UploadManagerDeps, 'uploadConfig'> & {
387
+ uploadConfig?: Partial<UploadConfig>;
388
+ }): UploadManagerDeps;
389
+
390
+ /**
391
+ * Cache Manager
392
+ *
393
+ * Handles cache eviction, cleanup logic, and cache management utilities.
394
+ *
395
+ * @module attachments/cache-manager
396
+ */
397
+
398
+ /**
399
+ * Ensure a path has the `file://` URI prefix.
400
+ * Used to standardize URI format throughout the attachment system.
401
+ */
402
+ declare function ensureFileUri(path: string): string;
403
+ /**
404
+ * Strip the `file://` prefix from a URI to get the raw file path.
405
+ * Used when APIs require raw paths instead of URIs.
406
+ */
407
+ declare function stripFileUri(uri: string): string;
408
+ /**
409
+ * Dependencies required by the cache manager.
410
+ */
411
+ interface CacheManagerDeps {
412
+ /** PowerSync database instance */
413
+ powersync: AbstractPowerSyncDatabase;
414
+ /** Platform adapter for file system operations */
415
+ platform: PlatformAdapter;
416
+ /** Logger adapter */
417
+ logger: LoggerAdapter;
418
+ /** Attachment table name */
419
+ tableName: string;
420
+ /** Cache configuration */
421
+ cacheConfig: CacheConfig;
422
+ /** Function to get local URI from local path */
423
+ getLocalUri: (localPath: string) => string;
424
+ /** Function to notify progress updates */
425
+ notify: (immediate: boolean) => void;
426
+ /** Function to invalidate stats cache */
427
+ invalidateStatsCache: () => void;
242
428
  }
243
- /** Row for eviction operations */
244
- interface EvictRow {
429
+ /**
430
+ * Get total size of cached files.
431
+ */
432
+ declare function getCachedSize(powersync: AbstractPowerSyncDatabase, tableName: string): Promise<number>;
433
+ /**
434
+ * Get files eligible for eviction, ordered by timestamp (oldest first).
435
+ */
436
+ declare function getEvictionCandidates(powersync: AbstractPowerSyncDatabase, tableName: string): Promise<Array<{
245
437
  id: string;
246
438
  local_uri: string;
247
439
  size: number;
248
- }
249
- /** Row for cached size queries */
250
- interface CachedSizeRow {
251
- total: number;
252
- }
253
- /** Row for ID queries */
254
- interface IdRow {
255
- id: string;
256
- }
440
+ }>>;
441
+ /**
442
+ * Enforce the cache size limit by evicting oldest files.
443
+ *
444
+ * This function checks if the current cache size exceeds `cacheConfig.maxSize`.
445
+ * If so, it evicts files (oldest first by timestamp) until the cache is below
446
+ * the max size. Evicted files have their `local_uri` cleared and state reset
447
+ * to `QUEUED_DOWNLOAD` so they can be re-downloaded later if needed.
448
+ *
449
+ * @returns Object with eviction stats: { evictedCount, freedBytes }
450
+ */
451
+ declare function enforceCacheLimit(deps: CacheManagerDeps): Promise<{
452
+ evictedCount: number;
453
+ freedBytes: number;
454
+ }>;
455
+ /**
456
+ * Check if cache is near capacity (at downloadStopThreshold).
457
+ * Used to decide whether to skip starting new downloads.
458
+ *
459
+ * @returns true if cache is at or above the download stop threshold
460
+ */
461
+ declare function isCacheNearCapacity(deps: CacheManagerDeps): Promise<boolean>;
462
+ /**
463
+ * Clear all cached files and re-queue for download.
464
+ * Preserves upload records (QUEUED_UPLOAD, FAILED_PERMANENT).
465
+ */
466
+ declare function clearCache(deps: CacheManagerDeps): Promise<void>;
467
+ /**
468
+ * Cache a local file into the attachment cache.
469
+ * Used to cache uploaded files to avoid redundant downloads.
470
+ */
471
+ declare function cacheLocalFile(deps: CacheManagerDeps, storagePath: string, sourceUri: string): Promise<void>;
472
+ /**
473
+ * Get the local file URI for a storage path.
474
+ * Checks both downloaded files (SYNCED state) and pending uploads (QUEUED_UPLOAD state).
475
+ */
476
+ declare function getLocalUriForStoragePath(deps: CacheManagerDeps, storagePath: string, storage: {
477
+ fileExists(uri: string): Promise<boolean>;
478
+ }): Promise<string | null>;
479
+ /**
480
+ * Copy source file to managed cache for durable uploads.
481
+ */
482
+ declare function copyToManagedCache(platform: PlatformAdapter, logger: LoggerAdapter, sourceUri: string, storagePath: string): Promise<string>;
483
+ /**
484
+ * Create cache manager dependencies with defaults.
485
+ */
486
+ declare function createCacheManagerDeps(partial: Omit<CacheManagerDeps, 'cacheConfig'> & {
487
+ cacheConfig?: Partial<CacheConfig>;
488
+ }): CacheManagerDeps;
257
489
 
258
490
  /**
259
- * Attachment Queue for @pol-studios/powersync
491
+ * Query Builder for Attachment Watch Queries
260
492
  *
261
- * Platform-agnostic attachment queue that handles:
262
- * - Parallel downloads with configurable concurrency
263
- * - Cache management with eviction
264
- * - Image compression
265
- * - Pause/resume functionality
266
- * - Progress tracking
493
+ * Generates SQL queries from WatchConfig objects.
494
+ * Provides type-safe query generation without raw SQL strings.
267
495
  */
268
496
 
269
497
  /**
270
- * Platform-agnostic attachment queue for offline file caching.
498
+ * Validates that a string is a safe SQL identifier.
499
+ * Throws an error if the identifier is invalid or potentially dangerous.
500
+ *
501
+ * @param identifier - The identifier to validate
502
+ * @param context - Description of where this identifier is used (for error messages)
503
+ * @throws Error if identifier is invalid
504
+ */
505
+ declare function validateSqlIdentifier(identifier: string, context: string): void;
506
+ /**
507
+ * Validates a WHERE clause fragment for basic safety.
508
+ * Note: This is a best-effort validation. Complex WHERE clauses should be reviewed.
509
+ *
510
+ * @param whereClause - The WHERE clause fragment to validate
511
+ * @throws Error if the clause contains dangerous patterns
512
+ */
513
+ declare function validateWhereClause(whereClause: string): void;
514
+ /**
515
+ * Build a SQL watch query from a WatchConfig.
516
+ *
517
+ * Generates a SELECT statement that:
518
+ * - Selects the ID column (aliased as `id`)
519
+ * - Optionally selects additional columns
520
+ * - Applies an optional WHERE clause
521
+ * - Optionally orders by a column
522
+ *
523
+ * @param config - The WatchConfig to build a query from
524
+ * @returns SQL query string
271
525
  *
272
526
  * @example
273
527
  * ```typescript
274
- * const queue = new AttachmentQueue({
275
- * powersync: db,
276
- * platform: platformAdapter,
277
- * config: {
278
- * source: {
279
- * table: 'EquipmentUnitMediaContent',
280
- * idColumn: 'storagePath',
281
- * orderByColumn: 'takenOn',
282
- * },
283
- * storage: storageAdapter,
284
- * },
528
+ * const query = buildWatchQuery({
529
+ * table: 'EquipmentUnitMediaContent',
530
+ * idColumn: 'storagePath',
531
+ * selectColumns: ['equipmentUnitId', 'takenOn'],
532
+ * where: 'storagePath IS NOT NULL',
533
+ * orderBy: { column: 'takenOn', direction: 'DESC' },
285
534
  * });
286
535
  *
287
- * await queue.init();
536
+ * // Result:
537
+ * // SELECT storagePath AS id, equipmentUnitId, takenOn
538
+ * // FROM EquipmentUnitMediaContent
539
+ * // WHERE storagePath IS NOT NULL
540
+ * // ORDER BY takenOn DESC
288
541
  * ```
289
542
  */
290
- declare class AttachmentQueue {
291
- private readonly powersync;
292
- private readonly platform;
293
- private readonly config;
294
- private readonly logger;
295
- private readonly tableName;
296
- private readonly concurrency;
297
- private readonly downloadTimeoutMs;
298
- private readonly retryDelayMs;
299
- private readonly downloadStopThreshold;
300
- private readonly evictionTriggerThreshold;
301
- private _initialized;
302
- private _paused;
303
- private _processing;
304
- private _cacheFull;
305
- private _resumeRequested;
306
- private _maxCacheSize;
307
- private _compressionQuality;
308
- private _abort;
309
- private _downloads;
310
- private _cachedSize;
311
- private _cachedSizeTimestamp;
312
- private _cachedStats;
313
- private _cachedStatsTimestamp;
314
- private _lastNotifyTime;
315
- private _notifyTimer;
316
- private _progressCallbacks;
317
- private _watcherInterval;
318
- constructor(options: {
319
- powersync: AbstractPowerSyncDatabase;
320
- platform: PlatformAdapter;
321
- config: AttachmentQueueConfig;
322
- });
323
- /**
324
- * Initialize the attachment queue.
325
- * Creates the attachment table if needed and starts watching for downloads.
326
- */
327
- init(): Promise<void>;
328
- /**
329
- * Dispose the attachment queue.
330
- */
331
- dispose(): void;
332
- /**
333
- * Subscribe to real-time progress updates.
334
- * Returns an unsubscribe function.
335
- */
336
- onProgress(callback: (stats: AttachmentSyncStats) => void): () => void;
337
- /** Whether downloads are paused */
338
- get paused(): boolean;
339
- /** Whether currently processing downloads */
340
- get processing(): boolean;
341
- /**
342
- * Set the maximum cache size.
343
- */
344
- setMaxCacheSize(bytes: number): Promise<void>;
345
- /**
346
- * Set the compression quality (0.1 to 1.0).
347
- */
348
- setCompressionQuality(quality: number): void;
349
- /** Get the current compression quality */
350
- getCompressionQuality(): number;
351
- /**
352
- * Pause downloads.
353
- */
354
- pause(): void;
355
- /**
356
- * Resume downloads.
357
- */
358
- resume(): void;
359
- /**
360
- * Get current sync statistics.
361
- */
362
- getStats(): Promise<AttachmentSyncStats>;
363
- /**
364
- * Clear all cached files and re-queue for download.
365
- */
366
- clearCache(): Promise<void>;
367
- /**
368
- * Get an attachment record by ID.
369
- */
370
- getRecord(id: string): Promise<AttachmentRecord | null>;
371
- /**
372
- * Get the local file URI for an attachment.
373
- */
374
- getLocalUri(localPath: string): string;
375
- /**
376
- * Cache a local file (e.g. one just uploaded) into the attachment cache.
377
- * This avoids a redundant download by copying the source file directly
378
- * into the cache directory and marking it as SYNCED.
379
- */
380
- cacheLocalFile(storagePath: string, sourceUri: string): Promise<void>;
381
- private _startDownloadWatcher;
382
- private _syncAttachmentIds;
383
- private _startDownloads;
384
- private _downloadOne;
385
- private _batchUpdateSizes;
386
- private _getIdsToDownload;
387
- private _evictIfNeeded;
388
- private _compressImage;
389
- private _isImage;
390
- private _getStatus;
391
- private _getCachedSizeWithCache;
392
- private _orderByNewest;
393
- private _sleep;
394
- private _withTimeout;
395
- private _blobToBase64;
396
- private _notify;
543
+ declare function buildWatchQuery(config: WatchConfig): string;
544
+ /**
545
+ * Build a simpler ID-only watch query.
546
+ * Use this when you only need IDs without additional columns.
547
+ *
548
+ * @param config - The WatchConfig to build a query from
549
+ * @returns SQL query string that selects only IDs
550
+ *
551
+ * @example
552
+ * ```typescript
553
+ * const query = buildIdOnlyWatchQuery({
554
+ * table: 'EquipmentUnitMediaContent',
555
+ * idColumn: 'storagePath',
556
+ * where: 'storagePath IS NOT NULL',
557
+ * });
558
+ *
559
+ * // Result:
560
+ * // SELECT storagePath AS id
561
+ * // FROM EquipmentUnitMediaContent
562
+ * // WHERE storagePath IS NOT NULL AND storagePath != ''
563
+ * ```
564
+ */
565
+ declare function buildIdOnlyWatchQuery(config: WatchConfig): string;
566
+ /**
567
+ * Build a query to fetch records with their IDs and additional columns.
568
+ * Used for populating the BatchFilterContext.records map.
569
+ *
570
+ * @param config - The WatchConfig to build a query from
571
+ * @param ids - Optional list of IDs to filter to (for efficiency)
572
+ * @returns SQL query string and parameters
573
+ *
574
+ * @example
575
+ * ```typescript
576
+ * const { query, params } = buildRecordFetchQuery(
577
+ * {
578
+ * table: 'EquipmentUnitMediaContent',
579
+ * idColumn: 'storagePath',
580
+ * selectColumns: ['equipmentUnitId'],
581
+ * },
582
+ * ['path/to/file1.jpg', 'path/to/file2.jpg']
583
+ * );
584
+ * ```
585
+ */
586
+ declare function buildRecordFetchQuery(config: WatchConfig, ids?: string[]): {
587
+ query: string;
588
+ params: unknown[];
589
+ };
590
+ /**
591
+ * Convert WatchConfig to the legacy AttachmentSourceConfig format.
592
+ * Useful during migration to maintain backwards compatibility.
593
+ *
594
+ * @param watchConfig - The new WatchConfig
595
+ * @returns Legacy AttachmentSourceConfig
596
+ */
597
+ declare function watchConfigToSourceConfig(watchConfig: WatchConfig): {
598
+ table: string;
599
+ idColumn: string;
600
+ orderByColumn?: string | null;
601
+ };
602
+
603
+ /**
604
+ * Migration Utilities for @pol-studios/powersync Attachments
605
+ *
606
+ * This module provides utilities for migrating from the old attachment API
607
+ * to the new callback-based API. It includes:
608
+ *
609
+ * - State mapping constants for old → new state transitions
610
+ * - `migrateAttachmentState()` for converting state values
611
+ * - Validation helpers for migration safety
612
+ *
613
+ * @example
614
+ * ```typescript
615
+ * import { migrateAttachmentState, isValidAttachmentState } from '@pol-studios/powersync/attachments';
616
+ *
617
+ * // Migrate a single state value
618
+ * const newState = migrateAttachmentState(oldState);
619
+ *
620
+ * // Validate before migration
621
+ * if (isValidAttachmentState(value)) {
622
+ * const migrated = migrateAttachmentState(value);
623
+ * }
624
+ * ```
625
+ */
626
+ /**
627
+ * Maps old state values to new state values.
628
+ *
629
+ * The official @powersync/attachments AttachmentState enum has values 0-4:
630
+ * QUEUED_SYNC=0, QUEUED_UPLOAD=1, QUEUED_DOWNLOAD=2, SYNCED=3, ARCHIVED=4
631
+ *
632
+ * POL extensions add:
633
+ * FAILED_PERMANENT=5, DOWNLOAD_SKIPPED=6
634
+ *
635
+ * For migration purposes, most states map 1:1. The mapping exists to:
636
+ * 1. Document the relationship between old and new states
637
+ * 2. Provide a clear upgrade path for custom state handling code
638
+ * 3. Allow future state reorganization if needed
639
+ */
640
+ declare const STATE_MAPPING: ReadonlyMap<number, number>;
641
+ /**
642
+ * Human-readable names for attachment states.
643
+ * Useful for logging and debugging during migration.
644
+ */
645
+ declare const STATE_NAMES: ReadonlyMap<number, string>;
646
+ /**
647
+ * All valid state values (official + POL extensions).
648
+ */
649
+ declare const VALID_STATES: ReadonlySet<number>;
650
+ /**
651
+ * States that indicate an active upload workflow.
652
+ * Records in these states should not be migrated to download states.
653
+ */
654
+ declare const UPLOAD_WORKFLOW_STATES: ReadonlySet<number>;
655
+ /**
656
+ * States that indicate an active download workflow.
657
+ */
658
+ declare const DOWNLOAD_WORKFLOW_STATES: ReadonlySet<number>;
659
+ /**
660
+ * Terminal states (no further processing needed).
661
+ */
662
+ declare const TERMINAL_STATES: ReadonlySet<number>;
663
+ /**
664
+ * Migrates an attachment state from the old API to the new API.
665
+ *
666
+ * Currently, this is a 1:1 mapping since the state values haven't changed.
667
+ * This function exists to:
668
+ * 1. Provide a clear migration path for apps using custom state handling
669
+ * 2. Document the state relationship
670
+ * 3. Allow future state reorganization without breaking existing code
671
+ *
672
+ * @param oldState - The state value from the old API
673
+ * @returns The corresponding state value in the new API
674
+ * @throws Error if the state value is invalid
675
+ *
676
+ * @example
677
+ * ```typescript
678
+ * import { migrateAttachmentState } from '@pol-studios/powersync/attachments';
679
+ *
680
+ * // Migrate a record's state
681
+ * const newState = migrateAttachmentState(record.state);
682
+ *
683
+ * // Migrate with fallback for unknown states
684
+ * const safeState = isValidAttachmentState(record.state)
685
+ * ? migrateAttachmentState(record.state)
686
+ * : PolAttachmentState.QUEUED_SYNC;
687
+ * ```
688
+ */
689
+ declare function migrateAttachmentState(oldState: number): number;
690
+ /**
691
+ * Safely migrates an attachment state with a fallback.
692
+ *
693
+ * Unlike `migrateAttachmentState`, this function never throws.
694
+ * Invalid states are mapped to the provided fallback.
695
+ *
696
+ * @param oldState - The state value from the old API
697
+ * @param fallback - State to use if oldState is invalid (default: QUEUED_SYNC)
698
+ * @returns The corresponding state value in the new API, or the fallback
699
+ *
700
+ * @example
701
+ * ```typescript
702
+ * // Safely migrate with QUEUED_SYNC as fallback
703
+ * const state = migrateAttachmentStateSafe(unknownValue);
704
+ *
705
+ * // Use custom fallback
706
+ * const state = migrateAttachmentStateSafe(unknownValue, PolAttachmentState.ARCHIVED);
707
+ * ```
708
+ */
709
+ declare function migrateAttachmentStateSafe(oldState: number, fallback?: number): number;
710
+ /**
711
+ * Checks if a value is a valid attachment state.
712
+ *
713
+ * @param value - The value to check
714
+ * @returns true if the value is a valid attachment state
715
+ *
716
+ * @example
717
+ * ```typescript
718
+ * if (isValidAttachmentState(record.state)) {
719
+ * // Safe to use record.state
720
+ * } else {
721
+ * console.warn(`Invalid state: ${record.state}`);
722
+ * }
723
+ * ```
724
+ */
725
+ declare function isValidAttachmentState(value: unknown): value is number;
726
+ /**
727
+ * Checks if a state represents an upload workflow.
728
+ *
729
+ * Records in upload workflow states should not be demoted to download states.
730
+ *
731
+ * @param state - The state to check
732
+ * @returns true if the state is part of an upload workflow
733
+ */
734
+ declare function isUploadWorkflowState(state: number): boolean;
735
+ /**
736
+ * Checks if a state represents a download workflow.
737
+ *
738
+ * @param state - The state to check
739
+ * @returns true if the state is part of a download workflow
740
+ */
741
+ declare function isDownloadWorkflowState(state: number): boolean;
742
+ /**
743
+ * Checks if a state is terminal (no further processing needed).
744
+ *
745
+ * @param state - The state to check
746
+ * @returns true if the state is terminal
747
+ */
748
+ declare function isTerminalState(state: number): boolean;
749
+ /**
750
+ * Gets the human-readable name of a state.
751
+ *
752
+ * @param state - The state value
753
+ * @returns The state name, or "UNKNOWN" for invalid states
754
+ *
755
+ * @example
756
+ * ```typescript
757
+ * console.log(`State: ${getStateName(record.state)}`); // "State: SYNCED"
758
+ * ```
759
+ */
760
+ declare function getStateName(state: number): string;
761
+ /**
762
+ * Statistics about a batch migration.
763
+ */
764
+ interface MigrationStats {
765
+ /** Total records processed */
766
+ total: number;
767
+ /** Records successfully migrated */
768
+ migrated: number;
769
+ /** Records with invalid states (used fallback) */
770
+ invalid: number;
771
+ /** Breakdown by state */
772
+ byState: Map<number, number>;
397
773
  }
774
+ /**
775
+ * Creates empty migration stats.
776
+ */
777
+ declare function createMigrationStats(): MigrationStats;
778
+ /**
779
+ * Records a migration result in the stats.
780
+ *
781
+ * @param stats - The stats object to update
782
+ * @param oldState - The original state
783
+ * @param newState - The migrated state
784
+ * @param wasValid - Whether the original state was valid
785
+ */
786
+ declare function recordMigration(stats: MigrationStats, oldState: number, newState: number, wasValid: boolean): void;
787
+ /**
788
+ * Formats migration stats as a human-readable summary.
789
+ *
790
+ * @param stats - The stats to format
791
+ * @returns A formatted string summary
792
+ *
793
+ * @example
794
+ * ```typescript
795
+ * const stats = createMigrationStats();
796
+ * // ... process records ...
797
+ * console.log(formatMigrationStats(stats));
798
+ * // Output:
799
+ * // Migration Summary:
800
+ * // Total: 100
801
+ * // Migrated: 98
802
+ * // Invalid (used fallback): 2
803
+ * // By State:
804
+ * // SYNCED: 50
805
+ * // QUEUED_DOWNLOAD: 30
806
+ * // QUEUED_UPLOAD: 18
807
+ * // QUEUED_SYNC: 2
808
+ * ```
809
+ */
810
+ declare function formatMigrationStats(stats: MigrationStats): string;
398
811
 
399
- export { AttachmentQueue, type AttachmentQueueConfig, type AttachmentRecord, type AttachmentSourceConfig, AttachmentState, type AttachmentStatsRow, type AttachmentStorageAdapter, type AttachmentSyncStats, type AttachmentSyncStatus, type CacheConfig, type CacheFileRow, type CachedSizeRow, type CompressionConfig, DEFAULT_CACHE_CONFIG, DEFAULT_COMPRESSION_CONFIG, DEFAULT_DOWNLOAD_CONFIG, type DownloadConfig, type DownloadPhase, type DownloadStatus, type EvictRow, type IdRow };
812
+ export { AttachmentStorageAdapter, CacheConfig, type CacheManagerDeps, DOWNLOAD_WORKFLOW_STATES, type DownloadManagerDeps, LOCALLY_AVAILABLE_STATES, type MigrationStats, PENDING_DOWNLOAD_STATES, PROTECTED_UPLOAD_STATES, PolAttachmentRecord, PolStorageAdapter, type PolStorageAdapterOptions, STATE_MAPPING, STATE_NAMES, TERMINAL_STATES, UPLOAD_WORKFLOW_STATES, UploadConfig, UploadHandler, type UploadManagerDeps, type UploadManagerState, UploadStatus, VALID_STATES, WatchConfig, blobToArrayBuffer, buildIdOnlyWatchQuery, buildRecordFetchQuery, buildWatchQuery, cacheLocalFile, clearCache, clearUploadCallback, copyToManagedCache, createCacheManagerDeps, createMigrationStats, createUploadManagerDeps, createUploadManagerState, determineAttachmentState, downloadRecord, enforceCacheLimit, ensureFileUri, extractErrorCode, formatMigrationStats, getCachedSize, getEvictionCandidates, getExcludeProtectedStatesCondition, getFailedPermanentUploads, getLocalUriForStoragePath, getPendingUploads, getProtectedStatesInClause, getSoonestRetryTime, getStaleUploads, getStateName, getSyncedUploadsWithPendingCallback, isCacheNearCapacity, isDownloadWorkflowState, isLocallyAvailable, isPendingDownloadState, isPermanentError, isProtectedUploadState, isStateTransitionAllowed, isTerminalState, isUploadWorkflowState, isValidAttachmentState, markUploadPermanentFailure, markUploadSynced, migrateAttachmentState, migrateAttachmentStateSafe, recordMigration, scheduleUploadRetry, startUploadProcessing, stripFileUri, uploadOne, validateSqlIdentifier, validateSqlIdentifier$1 as validateSqlIdentifierFromStateMachine, validateWhereClause, watchConfigToSourceConfig };