@pol-studios/powersync 1.0.7 → 1.0.11

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 (103) 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 +709 -6
  4. package/dist/attachments/index.js +133 -5
  5. package/dist/chunk-24RDMMCL.js +44 -0
  6. package/dist/chunk-24RDMMCL.js.map +1 -0
  7. package/dist/chunk-4TXTAEF2.js +2060 -0
  8. package/dist/chunk-4TXTAEF2.js.map +1 -0
  9. package/dist/chunk-63PXSPIN.js +358 -0
  10. package/dist/chunk-63PXSPIN.js.map +1 -0
  11. package/dist/chunk-654ERHA7.js +1 -0
  12. package/dist/{chunk-BREGB4WL.js → chunk-BRXQNASY.js} +287 -335
  13. package/dist/chunk-BRXQNASY.js.map +1 -0
  14. package/dist/{chunk-DHYUBVP7.js → chunk-CAB26E6F.js} +20 -9
  15. package/dist/chunk-CAB26E6F.js.map +1 -0
  16. package/dist/{chunk-H772V6XQ.js → chunk-CUCAYK7Z.js} +7 -43
  17. package/dist/chunk-CUCAYK7Z.js.map +1 -0
  18. package/dist/{chunk-4C3RY5SU.js → chunk-HWSNV45P.js} +76 -1
  19. package/dist/chunk-HWSNV45P.js.map +1 -0
  20. package/dist/{chunk-HFOFLW5F.js → chunk-KN2IZERF.js} +139 -6
  21. package/dist/chunk-KN2IZERF.js.map +1 -0
  22. package/dist/{chunk-UEYRTLKE.js → chunk-P4HZA6ZT.js} +20 -9
  23. package/dist/chunk-P4HZA6ZT.js.map +1 -0
  24. package/dist/chunk-T4AO7JIG.js +1 -0
  25. package/dist/{chunk-XQAJM2MW.js → chunk-VACPAAQZ.js} +33 -2
  26. package/dist/{chunk-XQAJM2MW.js.map → chunk-VACPAAQZ.js.map} +1 -1
  27. package/dist/{chunk-53WH2JJV.js → chunk-WN5ZJ3E2.js} +5 -8
  28. package/dist/chunk-WN5ZJ3E2.js.map +1 -0
  29. package/dist/chunk-XAEII4ZX.js +456 -0
  30. package/dist/chunk-XAEII4ZX.js.map +1 -0
  31. package/dist/chunk-XOY2CJ67.js +289 -0
  32. package/dist/chunk-XOY2CJ67.js.map +1 -0
  33. package/dist/chunk-YHTZ7VMV.js +1 -0
  34. package/dist/{chunk-MKD2VCX3.js → chunk-Z6VOBGTU.js} +8 -8
  35. package/dist/chunk-Z6VOBGTU.js.map +1 -0
  36. package/dist/chunk-ZM4ENYMF.js +230 -0
  37. package/dist/chunk-ZM4ENYMF.js.map +1 -0
  38. package/dist/connector/index.d.ts +56 -3
  39. package/dist/connector/index.js +8 -5
  40. package/dist/core/index.d.ts +12 -1
  41. package/dist/core/index.js +3 -2
  42. package/dist/error/index.js +0 -1
  43. package/dist/generator/cli.js +527 -0
  44. package/dist/generator/index.d.ts +168 -0
  45. package/dist/generator/index.js +370 -0
  46. package/dist/generator/index.js.map +1 -0
  47. package/dist/index.d.ts +12 -10
  48. package/dist/index.js +191 -29
  49. package/dist/index.native.d.ts +11 -9
  50. package/dist/index.native.js +191 -29
  51. package/dist/index.web.d.ts +11 -9
  52. package/dist/index.web.js +191 -29
  53. package/dist/maintenance/index.js +0 -1
  54. package/dist/platform/index.js +0 -2
  55. package/dist/platform/index.js.map +1 -1
  56. package/dist/platform/index.native.js +1 -2
  57. package/dist/platform/index.web.js +0 -1
  58. package/dist/pol-attachment-queue-BVAIueoP.d.ts +817 -0
  59. package/dist/provider/index.d.ts +38 -34
  60. package/dist/provider/index.js +11 -12
  61. package/dist/react/index.d.ts +372 -0
  62. package/dist/react/index.js +25 -0
  63. package/dist/storage/index.d.ts +3 -3
  64. package/dist/storage/index.js +22 -8
  65. package/dist/storage/index.native.d.ts +3 -3
  66. package/dist/storage/index.native.js +21 -7
  67. package/dist/storage/index.web.d.ts +3 -3
  68. package/dist/storage/index.web.js +21 -7
  69. package/dist/storage/upload/index.d.ts +7 -8
  70. package/dist/storage/upload/index.js +3 -3
  71. package/dist/storage/upload/index.native.d.ts +7 -8
  72. package/dist/storage/upload/index.native.js +4 -3
  73. package/dist/storage/upload/index.web.d.ts +1 -4
  74. package/dist/storage/upload/index.web.js +3 -3
  75. package/dist/supabase-connector-T9vHq_3i.d.ts +202 -0
  76. package/dist/sync/index.js +3 -3
  77. package/dist/{supabase-connector-qLm-WHkM.d.ts → types-B212hgfA.d.ts} +48 -170
  78. package/dist/{types-BVacP54t.d.ts → types-CyvBaAl8.d.ts} +12 -4
  79. package/dist/types-D0WcHrq6.d.ts +234 -0
  80. package/package.json +28 -4
  81. package/dist/CacheSettingsManager-1exbOC6S.d.ts +0 -261
  82. package/dist/chunk-4C3RY5SU.js.map +0 -1
  83. package/dist/chunk-53WH2JJV.js.map +0 -1
  84. package/dist/chunk-BREGB4WL.js.map +0 -1
  85. package/dist/chunk-DGUM43GV.js +0 -11
  86. package/dist/chunk-DHYUBVP7.js.map +0 -1
  87. package/dist/chunk-GKF7TOMT.js +0 -1
  88. package/dist/chunk-H772V6XQ.js.map +0 -1
  89. package/dist/chunk-HFOFLW5F.js.map +0 -1
  90. package/dist/chunk-KGSFAE5B.js +0 -1
  91. package/dist/chunk-LNL64IJZ.js +0 -1
  92. package/dist/chunk-MKD2VCX3.js.map +0 -1
  93. package/dist/chunk-UEYRTLKE.js.map +0 -1
  94. package/dist/chunk-WQ5MPAVC.js +0 -449
  95. package/dist/chunk-WQ5MPAVC.js.map +0 -1
  96. package/dist/chunk-ZEOKPWUC.js +0 -1165
  97. package/dist/chunk-ZEOKPWUC.js.map +0 -1
  98. package/dist/pol-attachment-queue-C7YNXXhK.d.ts +0 -676
  99. package/dist/types-Bgvx7-E8.d.ts +0 -187
  100. /package/dist/{chunk-DGUM43GV.js.map → chunk-654ERHA7.js.map} +0 -0
  101. /package/dist/{chunk-GKF7TOMT.js.map → chunk-T4AO7JIG.js.map} +0 -0
  102. /package/dist/{chunk-KGSFAE5B.js.map → chunk-YHTZ7VMV.js.map} +0 -0
  103. /package/dist/{chunk-LNL64IJZ.js.map → react/index.js.map} +0 -0
@@ -1,9 +1,10 @@
1
- import { e as AttachmentStorageAdapter } from '../pol-attachment-queue-C7YNXXhK.js';
2
- export { t as AttachmentRecord, A as AttachmentSourceConfig, q as AttachmentStatsRow, p as AttachmentSyncStats, o as AttachmentSyncStatus, h as CacheConfig, r as CacheFileRow, s as CachedSizeRow, C as CompressionConfig, i as DEFAULT_CACHE_CONFIG, D as DEFAULT_COMPRESSION_CONFIG, f as DEFAULT_UPLOAD_CONFIG, k as DownloadPhase, l as DownloadStatus, E as EvictRow, I as IdRow, P as PolAttachmentQueue, j as PolAttachmentQueueConfig, a as PolAttachmentQueueOptions, d as PolAttachmentRecord, b as PolAttachmentState, U as UploadConfig, g as UploadHandler, m as UploadPhase, n as UploadStatus, c as createPolAttachmentQueue } from '../pol-attachment-queue-C7YNXXhK.js';
3
- import { StorageAdapter, EncodingType } from '@powersync/attachments';
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
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 } from '../platform/index.js';
6
- import '@powersync/common';
5
+ import { PlatformAdapter, LoggerAdapter } from '../platform/index.js';
6
+ import { AbstractPowerSyncDatabase } from '@powersync/common';
7
+ export { AbstractPowerSyncDatabase as PowerSyncDBInterface } from '@powersync/common';
7
8
  import '../types-CDqWh56B.js';
8
9
 
9
10
  /**
@@ -31,6 +32,13 @@ interface PolStorageAdapterOptions {
31
32
  * Defaults to 'attachments'.
32
33
  */
33
34
  attachmentDirectoryName?: string;
35
+ /**
36
+ * Optional logger for diagnostic messages.
37
+ * If provided, will log warnings when deprecated code paths are hit.
38
+ */
39
+ logger?: {
40
+ warn(message: string, ...args: unknown[]): void;
41
+ };
34
42
  }
35
43
  /**
36
44
  * Storage adapter that implements the official @powersync/attachments interface.
@@ -51,6 +59,7 @@ declare class PolStorageAdapter implements StorageAdapter {
51
59
  private readonly remoteStorage;
52
60
  private readonly attachmentDirectoryName;
53
61
  private readonly userStorageDirectory;
62
+ private readonly logger?;
54
63
  constructor(options: PolStorageAdapterOptions);
55
64
  /**
56
65
  * Upload a file to remote storage.
@@ -60,6 +69,13 @@ declare class PolStorageAdapter implements StorageAdapter {
60
69
  }): Promise<void>;
61
70
  /**
62
71
  * Download a file from remote storage.
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.
63
79
  */
64
80
  downloadFile(filePath: string): Promise<Blob>;
65
81
  /**
@@ -106,4 +122,691 @@ declare class PolStorageAdapter implements StorageAdapter {
106
122
  private _base64ToArrayBuffer;
107
123
  }
108
124
 
109
- export { AttachmentStorageAdapter, PolStorageAdapter, type PolStorageAdapterOptions };
125
+ /**
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
+ */
133
+ /**
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
148
+ */
149
+ declare const PROTECTED_UPLOAD_STATES: Set<number>;
150
+ /**
151
+ * States that indicate a download is pending or in progress.
152
+ */
153
+ declare const PENDING_DOWNLOAD_STATES: Set<number>;
154
+ /**
155
+ * States that indicate the attachment is locally available.
156
+ */
157
+ declare const LOCALLY_AVAILABLE_STATES: Set<number>;
158
+ /**
159
+ * Check if a record is in a protected upload state.
160
+ * Protected states should not be demoted to download states.
161
+ */
162
+ declare function isProtectedUploadState(state: number): boolean;
163
+ /**
164
+ * Check if a record is in a pending download state.
165
+ */
166
+ declare function isPendingDownloadState(state: number): boolean;
167
+ /**
168
+ * Check if a record has a locally available file.
169
+ */
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;
251
+ }>;
252
+ /** Function to notify progress updates */
253
+ notify: (immediate: boolean) => void;
254
+ /** Function to invalidate stats cache */
255
+ invalidateStatsCache: () => void;
256
+ }
257
+ /**
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).
263
+ */
264
+ declare function blobToArrayBuffer(blob: Blob): Promise<ArrayBuffer>;
265
+ /**
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.
275
+ */
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
+
287
+ /**
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;
309
+ }
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>>;
324
+ }
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;
428
+ }
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<{
437
+ id: string;
438
+ local_uri: string;
439
+ size: number;
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;
489
+
490
+ /**
491
+ * Query Builder for Attachment Watch Queries
492
+ *
493
+ * Generates SQL queries from WatchConfig objects.
494
+ * Provides type-safe query generation without raw SQL strings.
495
+ */
496
+
497
+ /**
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
525
+ *
526
+ * @example
527
+ * ```typescript
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' },
534
+ * });
535
+ *
536
+ * // Result:
537
+ * // SELECT storagePath AS id, equipmentUnitId, takenOn
538
+ * // FROM EquipmentUnitMediaContent
539
+ * // WHERE storagePath IS NOT NULL
540
+ * // ORDER BY takenOn DESC
541
+ * ```
542
+ */
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>;
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;
811
+
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 };