@@ -21,6 +21,14 @@ export interface AttachmentQueueOptions {
2121 * Whether to mark the initial watched attachment IDs to be synced
2222 */
2323 performInitialSync ?: boolean ;
24+ /**
25+ * How to handle download errors, return { retry: false } to ignore the download
26+ */
27+ onDownloadError ?: ( attachment : AttachmentRecord , exception : any ) => Promise < { retry ?: boolean } > ;
28+ /**
29+ * How to handle upload errors, return { retry: false } to ignore the upload
30+ */
31+ onUploadError ?: ( attachment : AttachmentRecord , exception : any ) => Promise < { retry ?: boolean } > ;
2432}
2533
2634export const DEFAULT_ATTACHMENT_QUEUE_OPTIONS : Partial < AttachmentQueueOptions > = {
@@ -106,11 +114,11 @@ export abstract class AbstractAttachmentQueue<T extends AttachmentQueueOptions =
106114 this . initialSync = false ;
107115 // Mark AttachmentIds for sync
108116 await this . powersync . execute (
109- `UPDATE
110- ${ this . table }
111- SET state = ${ AttachmentState . QUEUED_SYNC }
112- WHERE
113- state < ${ AttachmentState . SYNCED }
117+ `UPDATE
118+ ${ this . table }
119+ SET state = ${ AttachmentState . QUEUED_SYNC }
120+ WHERE
121+ state < ${ AttachmentState . SYNCED }
114122 AND
115123 id IN (${ _ids } )`
116124 ) ;
@@ -142,9 +150,12 @@ export abstract class AbstractAttachmentQueue<T extends AttachmentQueueOptions =
142150
143151 // 3. Attachment in database and not in AttachmentIds, mark as archived
144152 await this . powersync . execute (
145- `UPDATE ${ this . table } SET state = ${ AttachmentState . ARCHIVED } WHERE state < ${
146- AttachmentState . ARCHIVED
147- } AND id NOT IN (${ ids . map ( ( id ) => `'${ id } '` ) . join ( ',' ) } )`
153+ `UPDATE ${ this . table }
154+ SET state = ${ AttachmentState . ARCHIVED }
155+ WHERE
156+ state < ${ AttachmentState . ARCHIVED }
157+ AND
158+ id NOT IN (${ ids . map ( ( id ) => `'${ id } '` ) . join ( ',' ) } )`
148159 ) ;
149160 }
150161 }
@@ -179,7 +190,7 @@ export abstract class AbstractAttachmentQueue<T extends AttachmentQueueOptions =
179190 const timestamp = new Date ( ) . getTime ( ) ;
180191 await this . powersync . execute (
181192 `UPDATE ${ this . table }
182- SET
193+ SET
183194 timestamp = ?,
184195 filename = ?,
185196 local_uri = ?,
@@ -267,6 +278,13 @@ export abstract class AbstractAttachmentQueue<T extends AttachmentQueueOptions =
267278 await this . update ( { ...record , state : AttachmentState . SYNCED } ) ;
268279 return false ;
269280 }
281+ if ( this . options . onUploadError ) {
282+ const { retry } = await this . options . onUploadError ( record , e ) ;
283+ if ( ! retry ) {
284+ await this . update ( { ...record , state : AttachmentState . ARCHIVED } ) ;
285+ return true ;
286+ }
287+ }
270288 console . error ( `UploadAttachment error for record ${ JSON . stringify ( record , null , 2 ) } ` ) ;
271289 return false ;
272290 }
@@ -312,6 +330,13 @@ export abstract class AbstractAttachmentQueue<T extends AttachmentQueueOptions =
312330 console . debug ( `Downloaded attachment "${ record . id } "` ) ;
313331 return true ;
314332 } catch ( e ) {
333+ if ( this . options . onDownloadError ) {
334+ const { retry } = await this . options . onDownloadError ( record , e ) ;
335+ if ( ! retry ) {
336+ await this . update ( { ...record , state : AttachmentState . ARCHIVED } ) ;
337+ return true ;
338+ }
339+ }
315340 console . error ( `Download attachment error for record ${ JSON . stringify ( record , null , 2 ) } ` , e ) ;
316341 }
317342 return false ;
@@ -320,11 +345,11 @@ export abstract class AbstractAttachmentQueue<T extends AttachmentQueueOptions =
320345 async * idsToUpload ( ) : AsyncIterable < string [ ] > {
321346 for await ( const result of this . powersync . watch (
322347 `SELECT id
323- FROM ${ this . table }
348+ FROM ${ this . table }
324349 WHERE
325350 local_uri IS NOT NULL
326351 AND
327- (state = ${ AttachmentState . QUEUED_UPLOAD }
352+ (state = ${ AttachmentState . QUEUED_UPLOAD }
328353 OR
329354 state = ${ AttachmentState . QUEUED_SYNC } )` ,
330355 [ ]
@@ -374,9 +399,9 @@ export abstract class AbstractAttachmentQueue<T extends AttachmentQueueOptions =
374399 async getIdsToDownload ( ) : Promise < string [ ] > {
375400 const res = await this . powersync . getAll < { id : string } > (
376401 `SELECT id
377- FROM ${ this . table }
402+ FROM ${ this . table }
378403 WHERE
379- state = ${ AttachmentState . QUEUED_DOWNLOAD }
404+ state = ${ AttachmentState . QUEUED_DOWNLOAD }
380405 OR
381406 state = ${ AttachmentState . QUEUED_SYNC }
382407 ORDER BY timestamp ASC`
@@ -387,10 +412,10 @@ export abstract class AbstractAttachmentQueue<T extends AttachmentQueueOptions =
387412 async * idsToDownload ( ) : AsyncIterable < string [ ] > {
388413 for await ( const result of this . powersync . watch (
389414 `SELECT id
390- FROM ${ this . table }
391- WHERE
392- state = ${ AttachmentState . QUEUED_DOWNLOAD }
393- OR
415+ FROM ${ this . table }
416+ WHERE
417+ state = ${ AttachmentState . QUEUED_DOWNLOAD }
418+ OR
394419 state = ${ AttachmentState . QUEUED_SYNC } ` ,
395420 [ ]
396421 ) ) {
@@ -460,10 +485,10 @@ export abstract class AbstractAttachmentQueue<T extends AttachmentQueueOptions =
460485 }
461486
462487 async expireCache ( ) {
463- const res = await this . powersync . getAll < AttachmentRecord > ( `SELECT * FROM ${ this . table }
464- WHERE
488+ const res = await this . powersync . getAll < AttachmentRecord > ( `SELECT * FROM ${ this . table }
489+ WHERE
465490 state = ${ AttachmentState . SYNCED } OR state = ${ AttachmentState . ARCHIVED }
466- ORDER BY
491+ ORDER BY
467492 timestamp DESC
468493 LIMIT 100 OFFSET ${ this . options . cacheLimit } ` ) ;
469494
0 commit comments