@pol-studios/powersync 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/attachments/index.js +1 -1
- package/dist/{chunk-PANEMMTU.js → chunk-3AYXHQ4W.js} +17 -11
- package/dist/chunk-3AYXHQ4W.js.map +1 -0
- package/dist/{chunk-BJ36QDFN.js → chunk-7EMDVIZX.js} +1 -1
- package/dist/chunk-7EMDVIZX.js.map +1 -0
- package/dist/{chunk-MB2RC3NS.js → chunk-C2RSTGDC.js} +129 -89
- package/dist/chunk-C2RSTGDC.js.map +1 -0
- package/dist/{chunk-NPNBGCRC.js → chunk-EJ23MXPQ.js} +1 -1
- package/dist/{chunk-NPNBGCRC.js.map → chunk-EJ23MXPQ.js.map} +1 -1
- package/dist/{chunk-CHRTN5PF.js → chunk-FPTDATY5.js} +1 -1
- package/dist/chunk-FPTDATY5.js.map +1 -0
- package/dist/chunk-GMFDCVMZ.js +1285 -0
- package/dist/chunk-GMFDCVMZ.js.map +1 -0
- package/dist/chunk-OLHGI472.js +1 -0
- package/dist/chunk-OLHGI472.js.map +1 -0
- package/dist/{chunk-CFCK2LHI.js → chunk-OTJXIRWX.js} +45 -40
- package/dist/chunk-OTJXIRWX.js.map +1 -0
- package/dist/{chunk-GBGATW2S.js → chunk-V6LJ6MR2.js} +86 -95
- package/dist/chunk-V6LJ6MR2.js.map +1 -0
- package/dist/connector/index.d.ts +1 -1
- package/dist/connector/index.js +2 -2
- package/dist/core/index.js +2 -2
- package/dist/{index-D952Qr38.d.ts → index-Cb-NI0Ct.d.ts} +9 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +8 -9
- package/dist/index.native.d.ts +1 -1
- package/dist/index.native.js +9 -10
- package/dist/index.web.d.ts +1 -1
- package/dist/index.web.js +9 -10
- package/dist/platform/index.js.map +1 -1
- package/dist/platform/index.native.js +1 -1
- package/dist/platform/index.web.js +1 -1
- package/dist/provider/index.d.ts +6 -1
- package/dist/provider/index.js +6 -6
- package/dist/sync/index.js +3 -3
- package/package.json +3 -1
- package/dist/chunk-42IJ25Q4.js +0 -45
- package/dist/chunk-42IJ25Q4.js.map +0 -1
- package/dist/chunk-BJ36QDFN.js.map +0 -1
- package/dist/chunk-CFCK2LHI.js.map +0 -1
- package/dist/chunk-CHRTN5PF.js.map +0 -1
- package/dist/chunk-GBGATW2S.js.map +0 -1
- package/dist/chunk-H7HZMI4H.js +0 -925
- package/dist/chunk-H7HZMI4H.js.map +0 -1
- package/dist/chunk-MB2RC3NS.js.map +0 -1
- package/dist/chunk-PANEMMTU.js.map +0 -1
|
@@ -70,9 +70,18 @@ var AttachmentQueue = class {
|
|
|
70
70
|
this.config = options.config;
|
|
71
71
|
this.logger = options.platform.logger;
|
|
72
72
|
this.tableName = options.config.attachmentTableName ?? "photo_attachments";
|
|
73
|
-
const downloadConfig = {
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
const downloadConfig = {
|
|
74
|
+
...DEFAULT_DOWNLOAD_CONFIG,
|
|
75
|
+
...options.config.download
|
|
76
|
+
};
|
|
77
|
+
const cacheConfig = {
|
|
78
|
+
...DEFAULT_CACHE_CONFIG,
|
|
79
|
+
...options.config.cache
|
|
80
|
+
};
|
|
81
|
+
const compressionConfig = {
|
|
82
|
+
...DEFAULT_COMPRESSION_CONFIG,
|
|
83
|
+
...options.config.compression
|
|
84
|
+
};
|
|
76
85
|
this.concurrency = downloadConfig.concurrency;
|
|
77
86
|
this.downloadTimeoutMs = downloadConfig.timeoutMs;
|
|
78
87
|
this.retryDelayMs = downloadConfig.retryDelayMs;
|
|
@@ -147,9 +156,7 @@ var AttachmentQueue = class {
|
|
|
147
156
|
async setMaxCacheSize(bytes) {
|
|
148
157
|
const oldLimit = this._maxCacheSize;
|
|
149
158
|
this._maxCacheSize = bytes;
|
|
150
|
-
this.logger.info(
|
|
151
|
-
`[AttachmentQueue] Cache limit changed: ${Math.round(oldLimit / 1024 / 1024)}MB \u2192 ${Math.round(bytes / 1024 / 1024)}MB`
|
|
152
|
-
);
|
|
159
|
+
this.logger.info(`[AttachmentQueue] Cache limit changed: ${Math.round(oldLimit / 1024 / 1024)}MB \u2192 ${Math.round(bytes / 1024 / 1024)}MB`);
|
|
153
160
|
if (bytes < oldLimit) {
|
|
154
161
|
const currentSize = await this._getCachedSizeWithCache();
|
|
155
162
|
const downloadStopLimit = bytes * this.downloadStopThreshold;
|
|
@@ -222,10 +229,8 @@ var AttachmentQueue = class {
|
|
|
222
229
|
activeDownloads: [...this._downloads.values()]
|
|
223
230
|
};
|
|
224
231
|
}
|
|
225
|
-
const rows = await this.powersync.getAll(
|
|
226
|
-
|
|
227
|
-
FROM ${this.tableName} GROUP BY state`
|
|
228
|
-
);
|
|
232
|
+
const rows = await this.powersync.getAll(`SELECT state, COUNT(*) as cnt, COALESCE(SUM(size), 0) as sz
|
|
233
|
+
FROM ${this.tableName} GROUP BY state`);
|
|
229
234
|
let synced = 0;
|
|
230
235
|
let syncedSize = 0;
|
|
231
236
|
let pending = 0;
|
|
@@ -268,19 +273,14 @@ var AttachmentQueue = class {
|
|
|
268
273
|
await this._sleep(100, this._abort.signal);
|
|
269
274
|
attempts++;
|
|
270
275
|
}
|
|
271
|
-
const withFiles = await this.powersync.getAll(
|
|
272
|
-
`SELECT id, local_uri FROM ${this.tableName} WHERE local_uri IS NOT NULL`
|
|
273
|
-
);
|
|
276
|
+
const withFiles = await this.powersync.getAll(`SELECT id, local_uri FROM ${this.tableName} WHERE local_uri IS NOT NULL`);
|
|
274
277
|
for (const row of withFiles) {
|
|
275
278
|
try {
|
|
276
279
|
await this.platform.fileSystem.deleteFile(row.local_uri);
|
|
277
280
|
} catch {
|
|
278
281
|
}
|
|
279
282
|
}
|
|
280
|
-
await this.powersync.execute(
|
|
281
|
-
`UPDATE ${this.tableName} SET state = ?, local_uri = NULL, size = 0`,
|
|
282
|
-
[0 /* QUEUED_DOWNLOAD */]
|
|
283
|
-
);
|
|
283
|
+
await this.powersync.execute(`UPDATE ${this.tableName} SET state = ?, local_uri = NULL, size = 0`, [0 /* QUEUED_DOWNLOAD */]);
|
|
284
284
|
} finally {
|
|
285
285
|
this._cachedSize = 0;
|
|
286
286
|
this._cachedSizeTimestamp = 0;
|
|
@@ -298,10 +298,7 @@ var AttachmentQueue = class {
|
|
|
298
298
|
* Get an attachment record by ID.
|
|
299
299
|
*/
|
|
300
300
|
async getRecord(id) {
|
|
301
|
-
return this.powersync.get(
|
|
302
|
-
`SELECT * FROM ${this.tableName} WHERE id = ?`,
|
|
303
|
-
[id]
|
|
304
|
-
);
|
|
301
|
+
return this.powersync.get(`SELECT * FROM ${this.tableName} WHERE id = ?`, [id]);
|
|
305
302
|
}
|
|
306
303
|
/**
|
|
307
304
|
* Get the local file URI for an attachment.
|
|
@@ -329,7 +326,9 @@ var AttachmentQueue = class {
|
|
|
329
326
|
const localPath = storagePath.replace(/[^a-zA-Z0-9]/g, "_");
|
|
330
327
|
const localUri = this.getLocalUri(localPath);
|
|
331
328
|
const dir = localUri.substring(0, localUri.lastIndexOf("/"));
|
|
332
|
-
await this.platform.fileSystem.makeDirectory(dir, {
|
|
329
|
+
await this.platform.fileSystem.makeDirectory(dir, {
|
|
330
|
+
intermediates: true
|
|
331
|
+
});
|
|
333
332
|
await this.platform.fileSystem.copyFile(sourceUri, localUri);
|
|
334
333
|
const info = await this.platform.fileSystem.getFileInfo(localUri);
|
|
335
334
|
const size = info && info.exists ? info.size : 0;
|
|
@@ -346,11 +345,8 @@ var AttachmentQueue = class {
|
|
|
346
345
|
mov: "video/quicktime"
|
|
347
346
|
};
|
|
348
347
|
const mediaType = mediaTypeMap[ext] ?? "application/octet-stream";
|
|
349
|
-
await this.powersync.execute(
|
|
350
|
-
|
|
351
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
352
|
-
[storagePath, storagePath, mediaType, 3 /* SYNCED */, localPath, size, Date.now()]
|
|
353
|
-
);
|
|
348
|
+
await this.powersync.execute(`INSERT OR REPLACE INTO ${this.tableName} (id, filename, media_type, state, local_uri, size, timestamp)
|
|
349
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`, [storagePath, storagePath, mediaType, 3 /* SYNCED */, localPath, size, Date.now()]);
|
|
354
350
|
this._cachedStats = null;
|
|
355
351
|
this._cachedStatsTimestamp = 0;
|
|
356
352
|
this._cachedSizeTimestamp = 0;
|
|
@@ -362,10 +358,18 @@ var AttachmentQueue = class {
|
|
|
362
358
|
}
|
|
363
359
|
// ─── Download Watcher ──────────────────────────────────────────────────────
|
|
364
360
|
_startDownloadWatcher() {
|
|
365
|
-
const {
|
|
361
|
+
const {
|
|
362
|
+
table,
|
|
363
|
+
idColumn,
|
|
364
|
+
projectFilter
|
|
365
|
+
} = this.config.source;
|
|
366
366
|
let query;
|
|
367
367
|
if (projectFilter) {
|
|
368
|
-
const {
|
|
368
|
+
const {
|
|
369
|
+
foreignKey,
|
|
370
|
+
intermediaryTable,
|
|
371
|
+
projectForeignKey
|
|
372
|
+
} = projectFilter;
|
|
369
373
|
query = `
|
|
370
374
|
SELECT m.${idColumn} as id
|
|
371
375
|
FROM ${table} m
|
|
@@ -407,11 +411,8 @@ var AttachmentQueue = class {
|
|
|
407
411
|
for (const id of ids) {
|
|
408
412
|
const existing = await this.getRecord(id);
|
|
409
413
|
if (!existing) {
|
|
410
|
-
await this.powersync.execute(
|
|
411
|
-
|
|
412
|
-
VALUES (?, ?, ?, ?)`,
|
|
413
|
-
[id, id, 0 /* QUEUED_DOWNLOAD */, Date.now()]
|
|
414
|
-
);
|
|
414
|
+
await this.powersync.execute(`INSERT OR IGNORE INTO ${this.tableName} (id, filename, state, timestamp)
|
|
415
|
+
VALUES (?, ?, ?, ?)`, [id, id, 0 /* QUEUED_DOWNLOAD */, Date.now()]);
|
|
415
416
|
}
|
|
416
417
|
}
|
|
417
418
|
}
|
|
@@ -447,9 +448,7 @@ var AttachmentQueue = class {
|
|
|
447
448
|
break;
|
|
448
449
|
}
|
|
449
450
|
const chunk = ordered.slice(i, i + this.concurrency);
|
|
450
|
-
const results = await Promise.allSettled(
|
|
451
|
-
chunk.map((id) => this._downloadOne(id, signal))
|
|
452
|
-
);
|
|
451
|
+
const results = await Promise.allSettled(chunk.map((id) => this._downloadOne(id, signal)));
|
|
453
452
|
const successful = [];
|
|
454
453
|
for (const result of results) {
|
|
455
454
|
if (result.status === "fulfilled" && result.value) {
|
|
@@ -489,28 +488,26 @@ var AttachmentQueue = class {
|
|
|
489
488
|
phase: "downloading"
|
|
490
489
|
});
|
|
491
490
|
try {
|
|
492
|
-
const data = await this._withTimeout(
|
|
493
|
-
this.config.storage.downloadFile(record.filename),
|
|
494
|
-
this.downloadTimeoutMs,
|
|
495
|
-
`Download timeout: ${record.filename}`
|
|
496
|
-
);
|
|
491
|
+
const data = await this._withTimeout(this.config.storage.downloadFile(record.filename), this.downloadTimeoutMs, `Download timeout: ${record.filename}`);
|
|
497
492
|
if (signal.aborted) return null;
|
|
498
493
|
const localPath = `${id.replace(/[^a-zA-Z0-9]/g, "_")}`;
|
|
499
494
|
const localUri = this.getLocalUri(localPath);
|
|
500
495
|
const dir = localUri.substring(0, localUri.lastIndexOf("/"));
|
|
501
|
-
await this.platform.fileSystem.makeDirectory(dir, {
|
|
496
|
+
await this.platform.fileSystem.makeDirectory(dir, {
|
|
497
|
+
intermediates: true
|
|
498
|
+
});
|
|
502
499
|
const content = data instanceof Blob ? await this._blobToBase64(data) : data;
|
|
503
500
|
await this.platform.fileSystem.writeFile(localUri, content, "base64");
|
|
504
501
|
await this._compressImage(localUri, id, record.filename);
|
|
505
502
|
const info = await this.platform.fileSystem.getFileInfo(localUri);
|
|
506
503
|
if (info && info.exists) {
|
|
507
|
-
await this.powersync.execute(
|
|
508
|
-
`UPDATE ${this.tableName}
|
|
504
|
+
await this.powersync.execute(`UPDATE ${this.tableName}
|
|
509
505
|
SET state = ?, local_uri = ?, size = ?
|
|
510
|
-
WHERE id = ?`,
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
506
|
+
WHERE id = ?`, [3 /* SYNCED */, localPath, info.size, id]);
|
|
507
|
+
return {
|
|
508
|
+
id,
|
|
509
|
+
size: info.size
|
|
510
|
+
};
|
|
514
511
|
}
|
|
515
512
|
return null;
|
|
516
513
|
} catch (err) {
|
|
@@ -523,20 +520,17 @@ var AttachmentQueue = class {
|
|
|
523
520
|
}
|
|
524
521
|
async _batchUpdateSizes(results) {
|
|
525
522
|
if (results.length === 0) return;
|
|
526
|
-
for (const {
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
);
|
|
523
|
+
for (const {
|
|
524
|
+
id,
|
|
525
|
+
size
|
|
526
|
+
} of results) {
|
|
527
|
+
await this.powersync.execute(`UPDATE ${this.tableName} SET size = ? WHERE id = ? AND state = ?`, [size, id, 3 /* SYNCED */]);
|
|
531
528
|
}
|
|
532
529
|
this._cachedSizeTimestamp = 0;
|
|
533
530
|
}
|
|
534
531
|
async _getIdsToDownload() {
|
|
535
|
-
const result = await this.powersync.getAll(
|
|
536
|
-
|
|
537
|
-
WHERE state IN (?, ?)`,
|
|
538
|
-
[0 /* QUEUED_DOWNLOAD */, 1 /* QUEUED_SYNC */]
|
|
539
|
-
);
|
|
532
|
+
const result = await this.powersync.getAll(`SELECT id FROM ${this.tableName}
|
|
533
|
+
WHERE state IN (?, ?)`, [0 /* QUEUED_DOWNLOAD */, 1 /* QUEUED_SYNC */]);
|
|
540
534
|
return result.map((r) => r.id);
|
|
541
535
|
}
|
|
542
536
|
// ─── Eviction ──────────────────────────────────────────────────────────────
|
|
@@ -547,21 +541,16 @@ var AttachmentQueue = class {
|
|
|
547
541
|
if (currentSize <= evictionTrigger) return;
|
|
548
542
|
this.logger.info("[AttachmentQueue] Starting eviction...");
|
|
549
543
|
while (currentSize > targetSize) {
|
|
550
|
-
const toEvict = await this.powersync.getAll(
|
|
551
|
-
|
|
552
|
-
WHERE state = ${3 /* SYNCED */} AND size > 0 LIMIT 100`
|
|
553
|
-
);
|
|
544
|
+
const toEvict = await this.powersync.getAll(`SELECT id, local_uri, size FROM ${this.tableName}
|
|
545
|
+
WHERE state = ${3 /* SYNCED */} AND size > 0 LIMIT 100`);
|
|
554
546
|
if (toEvict.length === 0) break;
|
|
555
547
|
for (const row of toEvict) {
|
|
556
548
|
if (currentSize <= targetSize) break;
|
|
557
549
|
if (row.local_uri) {
|
|
558
550
|
try {
|
|
559
551
|
await this.platform.fileSystem.deleteFile(this.getLocalUri(row.local_uri));
|
|
560
|
-
await this.powersync.execute(
|
|
561
|
-
|
|
562
|
-
SET state = ?, local_uri = NULL, size = 0 WHERE id = ?`,
|
|
563
|
-
[0 /* QUEUED_DOWNLOAD */, row.id]
|
|
564
|
-
);
|
|
552
|
+
await this.powersync.execute(`UPDATE ${this.tableName}
|
|
553
|
+
SET state = ?, local_uri = NULL, size = 0 WHERE id = ?`, [0 /* QUEUED_DOWNLOAD */, row.id]);
|
|
565
554
|
currentSize -= row.size;
|
|
566
555
|
} catch {
|
|
567
556
|
}
|
|
@@ -580,7 +569,10 @@ var AttachmentQueue = class {
|
|
|
580
569
|
async _compressImage(localUri, id, filename) {
|
|
581
570
|
if (!this._isImage(filename)) return;
|
|
582
571
|
if (!this.platform.imageProcessor) return;
|
|
583
|
-
const compressionConfig = {
|
|
572
|
+
const compressionConfig = {
|
|
573
|
+
...DEFAULT_COMPRESSION_CONFIG,
|
|
574
|
+
...this.config.compression
|
|
575
|
+
};
|
|
584
576
|
if (!compressionConfig.enabled) return;
|
|
585
577
|
try {
|
|
586
578
|
const info = await this.platform.fileSystem.getFileInfo(localUri);
|
|
@@ -590,7 +582,10 @@ var AttachmentQueue = class {
|
|
|
590
582
|
if (originalSize < compressionConfig.targetSizeBytes) return;
|
|
591
583
|
const existing = this._downloads.get(id);
|
|
592
584
|
if (existing) {
|
|
593
|
-
this._downloads.set(id, {
|
|
585
|
+
this._downloads.set(id, {
|
|
586
|
+
...existing,
|
|
587
|
+
phase: "compressing"
|
|
588
|
+
});
|
|
594
589
|
this._notify(false);
|
|
595
590
|
}
|
|
596
591
|
const result = await this.platform.imageProcessor.compress(localUri, {
|
|
@@ -606,9 +601,7 @@ var AttachmentQueue = class {
|
|
|
606
601
|
}
|
|
607
602
|
await this.platform.fileSystem.deleteFile(localUri);
|
|
608
603
|
await this.platform.fileSystem.moveFile(result.uri, localUri);
|
|
609
|
-
this.logger.info(
|
|
610
|
-
`[AttachmentQueue] Compressed ${filename}: ${Math.round(originalSize / 1024)}KB \u2192 ${Math.round(compressedInfo.size / 1024)}KB`
|
|
611
|
-
);
|
|
604
|
+
this.logger.info(`[AttachmentQueue] Compressed ${filename}: ${Math.round(originalSize / 1024)}KB \u2192 ${Math.round(compressedInfo.size / 1024)}KB`);
|
|
612
605
|
} catch (err) {
|
|
613
606
|
this.logger.warn(`[AttachmentQueue] Compression failed for ${filename}:`, err);
|
|
614
607
|
}
|
|
@@ -629,27 +622,26 @@ var AttachmentQueue = class {
|
|
|
629
622
|
if (!forceRefresh && this._cachedSizeTimestamp > 0 && now - this._cachedSizeTimestamp < CACHE_SIZE_TTL_MS) {
|
|
630
623
|
return this._cachedSize;
|
|
631
624
|
}
|
|
632
|
-
const result = await this.powersync.get(
|
|
633
|
-
|
|
634
|
-
WHERE state = ${3 /* SYNCED */}`
|
|
635
|
-
);
|
|
625
|
+
const result = await this.powersync.get(`SELECT COALESCE(SUM(size), 0) as total FROM ${this.tableName}
|
|
626
|
+
WHERE state = ${3 /* SYNCED */}`);
|
|
636
627
|
this._cachedSize = result?.total ?? 0;
|
|
637
628
|
this._cachedSizeTimestamp = now;
|
|
638
629
|
return this._cachedSize;
|
|
639
630
|
}
|
|
640
631
|
async _orderByNewest(ids) {
|
|
641
632
|
if (ids.length <= 3) return ids;
|
|
642
|
-
const {
|
|
633
|
+
const {
|
|
634
|
+
table,
|
|
635
|
+
idColumn,
|
|
636
|
+
orderByColumn
|
|
637
|
+
} = this.config.source;
|
|
643
638
|
if (!orderByColumn) return ids;
|
|
644
639
|
try {
|
|
645
640
|
const idSet = new Set(ids);
|
|
646
|
-
const ordered = await this.powersync.getAll(
|
|
647
|
-
`SELECT ${idColumn} as id FROM ${table}
|
|
641
|
+
const ordered = await this.powersync.getAll(`SELECT ${idColumn} as id FROM ${table}
|
|
648
642
|
WHERE ${idColumn} IS NOT NULL AND ${idColumn} != ''
|
|
649
643
|
ORDER BY ${orderByColumn} DESC NULLS LAST
|
|
650
|
-
LIMIT ?`,
|
|
651
|
-
[ids.length * 2]
|
|
652
|
-
);
|
|
644
|
+
LIMIT ?`, [ids.length * 2]);
|
|
653
645
|
const result = [];
|
|
654
646
|
for (const row of ordered) {
|
|
655
647
|
if (idSet.has(row.id)) {
|
|
@@ -677,22 +669,21 @@ var AttachmentQueue = class {
|
|
|
677
669
|
signal.removeEventListener("abort", onAbort);
|
|
678
670
|
resolve();
|
|
679
671
|
}, ms);
|
|
680
|
-
signal.addEventListener("abort", onAbort, {
|
|
672
|
+
signal.addEventListener("abort", onAbort, {
|
|
673
|
+
once: true
|
|
674
|
+
});
|
|
681
675
|
});
|
|
682
676
|
}
|
|
683
677
|
_withTimeout(promise, ms, message) {
|
|
684
678
|
return new Promise((resolve, reject) => {
|
|
685
679
|
const timer = setTimeout(() => reject(new Error(message)), ms);
|
|
686
|
-
promise.then(
|
|
687
|
-
(
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
(err)
|
|
692
|
-
|
|
693
|
-
reject(err);
|
|
694
|
-
}
|
|
695
|
-
);
|
|
680
|
+
promise.then((result) => {
|
|
681
|
+
clearTimeout(timer);
|
|
682
|
+
resolve(result);
|
|
683
|
+
}, (err) => {
|
|
684
|
+
clearTimeout(timer);
|
|
685
|
+
reject(err);
|
|
686
|
+
});
|
|
696
687
|
});
|
|
697
688
|
}
|
|
698
689
|
async _blobToBase64(blob) {
|
|
@@ -746,4 +737,4 @@ export {
|
|
|
746
737
|
DEFAULT_CACHE_CONFIG,
|
|
747
738
|
AttachmentQueue
|
|
748
739
|
};
|
|
749
|
-
//# sourceMappingURL=chunk-
|
|
740
|
+
//# sourceMappingURL=chunk-V6LJ6MR2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/attachments/types.ts","../src/attachments/attachment-queue.ts"],"sourcesContent":["/**\n * Attachment Queue Types for @pol-studios/powersync\n *\n * Defines interfaces for the attachment queue system that handles\n * offline file caching with download, compression, and eviction.\n */\n\n// ─── Attachment States ───────────────────────────────────────────────────────\n\n/**\n * State of an attachment in the queue.\n * NOTE: These do NOT match @powersync/attachments enum ordering (values 0-2 differ).\n */\nexport enum AttachmentState {\n /** Waiting to be downloaded */\n QUEUED_DOWNLOAD = 0,\n /** Waiting for initial sync */\n QUEUED_SYNC = 1,\n /** Waiting to be uploaded */\n QUEUED_UPLOAD = 2,\n /** Fully synced (downloaded or uploaded) */\n SYNCED = 3,\n /** Archived (removed from sync but record kept) */\n ARCHIVED = 4,\n}\n\n// ─── Attachment Record ───────────────────────────────────────────────────────\n\n/**\n * Record representing an attachment in the queue database.\n */\nexport interface AttachmentRecord {\n /** Unique identifier (typically storage path) */\n id: string;\n /** Filename for display and type inference */\n filename: string;\n /** MIME type of the file */\n media_type: string;\n /** Current state in the queue */\n state: AttachmentState;\n /** Local file URI (set after download) */\n local_uri?: string | null;\n /** File size in bytes */\n size?: number;\n /** Timestamp when the attachment was created */\n timestamp?: number;\n}\n\n// ─── Source Table Configuration ──────────────────────────────────────────────\n\n/**\n * Configuration for the source table that contains attachment references.\n * This makes the attachment queue reusable across different tables/projects.\n */\nexport interface AttachmentSourceConfig {\n /**\n * Table name containing attachment references.\n * @example \"EquipmentUnitMediaContent\"\n */\n table: string;\n\n /**\n * Column containing the storage path / attachment ID.\n * @example \"storagePath\"\n */\n idColumn: string;\n\n /**\n * Column to order by for \"newest first\" downloads.\n * Set to null to skip ordering.\n * @example \"takenOn\"\n */\n orderByColumn: string | null;\n\n /**\n * Optional filter config to exclude attachments from archived/unsynced projects.\n * When set, the attachment query JOINs through intermediary tables to ensure\n * only attachments belonging to synced projects are downloaded.\n */\n projectFilter?: {\n /** Foreign key column in the source table (e.g., \"equipmentUnitId\") */\n foreignKey: string;\n /** Intermediary table to JOIN through (e.g., \"EquipmentFixtureUnit\") */\n intermediaryTable: string;\n /** Column in intermediary table that links to ProjectDatabase (e.g., \"projectDatabaseId\") */\n projectForeignKey: string;\n };\n}\n\n// ─── Storage Adapter ─────────────────────────────────────────────────────────\n\n/**\n * Interface for attachment storage operations (e.g., Supabase Storage).\n */\nexport interface AttachmentStorageAdapter {\n /**\n * Download a file from remote storage.\n * @param filePath - The storage path of the file\n * @returns The file data as a Blob or base64 string\n */\n downloadFile(filePath: string): Promise<Blob | string>;\n\n /**\n * Upload a file to remote storage (optional - not all queues need upload).\n * @param filePath - The storage path to upload to\n * @param data - The file data to upload\n */\n uploadFile?(filePath: string, data: Blob | string): Promise<void>;\n\n /**\n * Delete a file from remote storage (optional).\n * @param filePath - The storage path of the file\n */\n deleteFile?(filePath: string): Promise<void>;\n\n /**\n * Resolve the storage bucket for a file path.\n * Allows routing different files to different buckets.\n * @param filePath - The file path to resolve\n * @returns The bucket name\n */\n resolveBucket?(filePath: string): string;\n}\n\n// ─── Compression Configuration ───────────────────────────────────────────────\n\n/**\n * Configuration for image compression.\n */\nexport interface CompressionConfig {\n /** Enable compression (default: true) */\n enabled: boolean;\n /** Compression quality 0.0-1.0 (default: 0.7) */\n quality: number;\n /** Max width before resizing (default: 2048) */\n maxWidth: number;\n /** Skip files under this size in bytes (default: 100KB) */\n skipSizeBytes: number;\n /** Skip if already under this size in bytes (default: 300KB) */\n targetSizeBytes: number;\n}\n\n/**\n * Default compression configuration.\n */\nexport const DEFAULT_COMPRESSION_CONFIG: CompressionConfig = {\n enabled: true,\n quality: 0.7,\n maxWidth: 2048,\n skipSizeBytes: 100_000,\n targetSizeBytes: 300_000\n};\n\n// ─── Download Configuration ──────────────────────────────────────────────────\n\n/**\n * Configuration for the download engine.\n */\nexport interface DownloadConfig {\n /** Maximum concurrent downloads (default: 50) */\n concurrency: number;\n /** Download timeout per file in ms (default: 60000) */\n timeoutMs: number;\n /** Retry delay between batches in ms (default: 5000) */\n retryDelayMs: number;\n}\n\n/**\n * Default download configuration.\n */\nexport const DEFAULT_DOWNLOAD_CONFIG: DownloadConfig = {\n concurrency: 50,\n timeoutMs: 60_000,\n retryDelayMs: 5_000\n};\n\n// ─── Cache Configuration ─────────────────────────────────────────────────────\n\n/**\n * Configuration for cache management.\n */\nexport interface CacheConfig {\n /** Maximum cache size in bytes (default: 5GB) */\n maxSize: number;\n /** Stop downloads at this percentage of max (default: 0.95 = 95%) */\n downloadStopThreshold: number;\n /** Trigger eviction at this percentage (default: 1.0 = 100%) */\n evictionTriggerThreshold: number;\n}\n\n/**\n * Default cache configuration.\n */\nexport const DEFAULT_CACHE_CONFIG: CacheConfig = {\n maxSize: 5 * 1024 * 1024 * 1024,\n // 5 GB\n downloadStopThreshold: 0.95,\n evictionTriggerThreshold: 1.0\n};\n\n// ─── Attachment Queue Configuration ──────────────────────────────────────────\n\n/**\n * Full configuration for the attachment queue.\n */\nexport interface AttachmentQueueConfig {\n /** Source table configuration */\n source: AttachmentSourceConfig;\n\n /** Storage adapter for downloading files */\n storage: AttachmentStorageAdapter;\n\n /** Table name for storing attachment records (default: \"photo_attachments\") */\n attachmentTableName?: string;\n\n /** Perform initial sync on initialization (default: true) */\n performInitialSync?: boolean;\n\n /** Download configuration */\n download?: Partial<DownloadConfig>;\n\n /** Cache configuration */\n cache?: Partial<CacheConfig>;\n\n /** Compression configuration */\n compression?: Partial<CompressionConfig>;\n\n /**\n * Called when a download fails.\n * Return { retry: true } to retry, { retry: false } to skip.\n */\n onDownloadError?: (attachment: AttachmentRecord, error: Error) => Promise<{\n retry: boolean;\n }>;\n\n /**\n * Called when sync progress changes.\n */\n onProgress?: (stats: AttachmentSyncStats) => void;\n}\n\n// ─── Download Status Types ───────────────────────────────────────────────────\n\n/**\n * Current phase of a download operation.\n */\nexport type DownloadPhase = 'downloading' | 'compressing' | 'complete' | 'error';\n\n/**\n * Status of an individual download.\n */\nexport interface DownloadStatus {\n /** Attachment ID */\n id: string;\n /** Filename being downloaded */\n filename: string;\n /** Current phase */\n phase: DownloadPhase;\n}\n\n// ─── Sync Status Types ───────────────────────────────────────────────────────\n\n/**\n * Why downloads are stopped (if not actively syncing).\n */\nexport type AttachmentSyncStatus = 'syncing' // Actively downloading\n| 'paused' // User manually paused\n| 'cache_full' // Stopped because cache hit capacity\n| 'complete'; // All attachments downloaded\n\n/**\n * Statistics about the attachment sync progress.\n */\nexport interface AttachmentSyncStats {\n /** Number of attachments that have been downloaded */\n syncedCount: number;\n /** Total size of synced attachments in bytes */\n syncedSize: number;\n /** Number of attachments waiting to be downloaded */\n pendingCount: number;\n /** Total expected attachments (synced + pending) */\n totalExpected: number;\n /** Maximum cache size in bytes */\n maxCacheSize: number;\n /** Current compression quality (0.1 to 1.0) */\n compressionQuality: number;\n /** Current sync status */\n status: AttachmentSyncStatus;\n /** Whether downloads are paused */\n isPaused: boolean;\n /** Whether currently processing downloads */\n isProcessing: boolean;\n /** Currently active downloads */\n activeDownloads: DownloadStatus[];\n}\n\n// ─── SQL Row Types (for internal use) ────────────────────────────────────────\n\n/** Row from stats query */\nexport interface AttachmentStatsRow {\n state: number;\n cnt: number;\n sz: number;\n}\n\n/** Row for cache file operations */\nexport interface CacheFileRow {\n id: string;\n local_uri: string;\n}\n\n/** Row for eviction operations */\nexport interface EvictRow {\n id: string;\n local_uri: string;\n size: number;\n}\n\n/** Row for cached size queries */\nexport interface CachedSizeRow {\n total: number;\n}\n\n/** Row for ID queries */\nexport interface IdRow {\n id: string;\n}","/**\n * Attachment Queue for @pol-studios/powersync\n *\n * Platform-agnostic attachment queue that handles:\n * - Parallel downloads with configurable concurrency\n * - Cache management with eviction\n * - Image compression\n * - Pause/resume functionality\n * - Progress tracking\n */\n\nimport type { AbstractPowerSyncDatabase } from '../core/types';\nimport type { PlatformAdapter, LoggerAdapter } from '../platform/types';\nimport { AttachmentState, type AttachmentRecord, type AttachmentQueueConfig, type AttachmentSyncStats, type AttachmentSyncStatus, type DownloadStatus, type DownloadPhase, type AttachmentStatsRow, type CacheFileRow, type EvictRow, type CachedSizeRow, type IdRow, DEFAULT_COMPRESSION_CONFIG, DEFAULT_DOWNLOAD_CONFIG, DEFAULT_CACHE_CONFIG } from './types';\n\n// ─── Internal Types ──────────────────────────────────────────────────────────\n\ninterface DownloadResult {\n id: string;\n size: number;\n}\n\n// ─── Constants ───────────────────────────────────────────────────────────────\n\nconst NOTIFY_THROTTLE_MS = 500;\nconst STATS_CACHE_TTL_MS = 500;\nconst CACHE_SIZE_TTL_MS = 5000;\n\n/**\n * Platform-agnostic attachment queue for offline file caching.\n *\n * @example\n * ```typescript\n * const queue = new AttachmentQueue({\n * powersync: db,\n * platform: platformAdapter,\n * config: {\n * source: {\n * table: 'EquipmentUnitMediaContent',\n * idColumn: 'storagePath',\n * orderByColumn: 'takenOn',\n * },\n * storage: storageAdapter,\n * },\n * });\n *\n * await queue.init();\n * ```\n */\nexport class AttachmentQueue {\n // ─── Dependencies ──────────────────────────────────────────────────────────\n\n private readonly powersync: AbstractPowerSyncDatabase;\n private readonly platform: PlatformAdapter;\n private readonly config: AttachmentQueueConfig;\n private readonly logger: LoggerAdapter;\n private readonly tableName: string;\n\n // ─── Configuration ─────────────────────────────────────────────────────────\n\n private readonly concurrency: number;\n private readonly downloadTimeoutMs: number;\n private readonly retryDelayMs: number;\n private readonly downloadStopThreshold: number;\n private readonly evictionTriggerThreshold: number;\n\n // ─── State ─────────────────────────────────────────────────────────────────\n\n private _initialized = false;\n private _paused = false;\n private _processing = false;\n private _cacheFull = false;\n private _resumeRequested = false;\n private _maxCacheSize: number;\n private _compressionQuality: number;\n private _abort = new AbortController();\n private _downloads = new Map<string, DownloadStatus>();\n\n // ─── Caching ───────────────────────────────────────────────────────────────\n\n private _cachedSize = 0;\n private _cachedSizeTimestamp = 0;\n private _cachedStats: AttachmentSyncStats | null = null;\n private _cachedStatsTimestamp = 0;\n\n // ─── Notification ──────────────────────────────────────────────────────────\n\n private _lastNotifyTime = 0;\n private _notifyTimer: ReturnType<typeof setTimeout> | null = null;\n private _progressCallbacks = new Set<(stats: AttachmentSyncStats) => void>();\n private _watcherInterval: ReturnType<typeof setInterval> | null = null;\n\n // ─── Constructor ───────────────────────────────────────────────────────────\n\n constructor(options: {\n powersync: AbstractPowerSyncDatabase;\n platform: PlatformAdapter;\n config: AttachmentQueueConfig;\n }) {\n this.powersync = options.powersync;\n this.platform = options.platform;\n this.config = options.config;\n this.logger = options.platform.logger;\n this.tableName = options.config.attachmentTableName ?? 'photo_attachments';\n\n // Merge with defaults\n const downloadConfig = {\n ...DEFAULT_DOWNLOAD_CONFIG,\n ...options.config.download\n };\n const cacheConfig = {\n ...DEFAULT_CACHE_CONFIG,\n ...options.config.cache\n };\n const compressionConfig = {\n ...DEFAULT_COMPRESSION_CONFIG,\n ...options.config.compression\n };\n this.concurrency = downloadConfig.concurrency;\n this.downloadTimeoutMs = downloadConfig.timeoutMs;\n this.retryDelayMs = downloadConfig.retryDelayMs;\n this.downloadStopThreshold = cacheConfig.downloadStopThreshold;\n this.evictionTriggerThreshold = cacheConfig.evictionTriggerThreshold;\n this._maxCacheSize = cacheConfig.maxSize;\n this._compressionQuality = compressionConfig.quality;\n }\n\n // ─── Initialization ────────────────────────────────────────────────────────\n\n /**\n * Initialize the attachment queue.\n * Creates the attachment table if needed and starts watching for downloads.\n */\n async init(): Promise<void> {\n if (this._initialized) return;\n this.logger.info('[AttachmentQueue] Initializing...');\n\n // Create attachment table\n await this.powersync.execute(`\n CREATE TABLE IF NOT EXISTS ${this.tableName} (\n id TEXT PRIMARY KEY,\n filename TEXT NOT NULL,\n media_type TEXT,\n state INTEGER NOT NULL DEFAULT ${AttachmentState.QUEUED_DOWNLOAD},\n local_uri TEXT,\n size INTEGER DEFAULT 0,\n timestamp INTEGER\n )\n `);\n this._initialized = true;\n\n // Start watching for downloads if configured\n if (this.config.performInitialSync !== false) {\n this._startDownloadWatcher();\n }\n this.logger.info('[AttachmentQueue] Initialized');\n }\n\n /**\n * Dispose the attachment queue.\n */\n dispose(): void {\n this._abort.abort();\n if (this._watcherInterval) {\n clearInterval(this._watcherInterval);\n this._watcherInterval = null;\n }\n if (this._notifyTimer) {\n clearTimeout(this._notifyTimer);\n }\n this._progressCallbacks.clear();\n this._initialized = false;\n }\n\n // ─── Public API ────────────────────────────────────────────────────────────\n\n /**\n * Subscribe to real-time progress updates.\n * Returns an unsubscribe function.\n */\n onProgress(callback: (stats: AttachmentSyncStats) => void): () => void {\n this._progressCallbacks.add(callback);\n // Immediately notify with current stats\n this._notify(true);\n return () => {\n this._progressCallbacks.delete(callback);\n };\n }\n\n /** Whether downloads are paused */\n get paused(): boolean {\n return this._paused;\n }\n\n /** Whether currently processing downloads */\n get processing(): boolean {\n return this._processing;\n }\n\n /**\n * Set the maximum cache size.\n */\n async setMaxCacheSize(bytes: number): Promise<void> {\n const oldLimit = this._maxCacheSize;\n this._maxCacheSize = bytes;\n this.logger.info(`[AttachmentQueue] Cache limit changed: ${Math.round(oldLimit / 1024 / 1024)}MB → ${Math.round(bytes / 1024 / 1024)}MB`);\n if (bytes < oldLimit) {\n const currentSize = await this._getCachedSizeWithCache();\n const downloadStopLimit = bytes * this.downloadStopThreshold;\n if (currentSize >= downloadStopLimit) {\n this._cacheFull = true;\n this._abort.abort();\n this._abort = new AbortController();\n this._notify(true);\n await this._evictIfNeeded();\n } else {\n this._notify(true);\n }\n } else if (bytes > oldLimit && this._cacheFull) {\n this._cacheFull = false;\n this._notify(true);\n if (!this._paused && !this._processing) {\n this._startDownloads();\n }\n } else {\n this._notify(true);\n }\n }\n\n /**\n * Set the compression quality (0.1 to 1.0).\n */\n setCompressionQuality(quality: number): void {\n this._compressionQuality = Math.max(0.1, Math.min(1.0, quality));\n this._notify(true);\n }\n\n /** Get the current compression quality */\n getCompressionQuality(): number {\n return this._compressionQuality;\n }\n\n /**\n * Pause downloads.\n */\n pause(): void {\n this.logger.info('[AttachmentQueue] Pausing downloads');\n this._paused = true;\n this._abort.abort();\n this._abort = new AbortController();\n this._notify(true);\n }\n\n /**\n * Resume downloads.\n */\n resume(): void {\n this.logger.info('[AttachmentQueue] Resuming downloads');\n this._paused = false;\n this._abort = new AbortController();\n this._notify(true);\n if (this._processing) {\n this._resumeRequested = true;\n } else {\n this._startDownloads();\n }\n }\n\n /**\n * Get current sync statistics.\n */\n async getStats(): Promise<AttachmentSyncStats> {\n const now = Date.now();\n if (this._cachedStats && now - this._cachedStatsTimestamp < STATS_CACHE_TTL_MS) {\n return {\n ...this._cachedStats,\n compressionQuality: this._compressionQuality,\n status: this._getStatus(this._cachedStats.pendingCount),\n isPaused: this._paused,\n isProcessing: this._processing,\n activeDownloads: [...this._downloads.values()]\n };\n }\n const rows = await this.powersync.getAll<AttachmentStatsRow>(`SELECT state, COUNT(*) as cnt, COALESCE(SUM(size), 0) as sz\n FROM ${this.tableName} GROUP BY state`);\n let synced = 0;\n let syncedSize = 0;\n let pending = 0;\n for (const r of rows) {\n if (r.state === AttachmentState.SYNCED) {\n synced = r.cnt;\n syncedSize = r.sz;\n }\n if (r.state === AttachmentState.QUEUED_DOWNLOAD || r.state === AttachmentState.QUEUED_SYNC) {\n pending += r.cnt;\n }\n }\n this._cachedSize = syncedSize;\n this._cachedSizeTimestamp = now;\n const stats: AttachmentSyncStats = {\n syncedCount: synced,\n syncedSize,\n pendingCount: pending,\n totalExpected: synced + pending,\n maxCacheSize: this._maxCacheSize,\n compressionQuality: this._compressionQuality,\n status: this._getStatus(pending),\n isPaused: this._paused,\n isProcessing: this._processing,\n activeDownloads: [...this._downloads.values()]\n };\n this._cachedStats = stats;\n this._cachedStatsTimestamp = now;\n return stats;\n }\n\n /**\n * Clear all cached files and re-queue for download.\n */\n async clearCache(): Promise<void> {\n const wasPaused = this._paused;\n if (!wasPaused) this.pause();\n try {\n // Wait for processing to stop\n let attempts = 0;\n while (this._processing && attempts < 50) {\n await this._sleep(100, this._abort.signal);\n attempts++;\n }\n\n // Delete all cached files\n const withFiles = await this.powersync.getAll<CacheFileRow>(`SELECT id, local_uri FROM ${this.tableName} WHERE local_uri IS NOT NULL`);\n for (const row of withFiles) {\n try {\n await this.platform.fileSystem.deleteFile(row.local_uri);\n } catch {\n // File may already be gone\n }\n }\n\n // Re-queue all records\n await this.powersync.execute(`UPDATE ${this.tableName} SET state = ?, local_uri = NULL, size = 0`, [AttachmentState.QUEUED_DOWNLOAD]);\n } finally {\n this._cachedSize = 0;\n this._cachedSizeTimestamp = 0;\n this._cachedStats = null;\n this._cachedStatsTimestamp = 0;\n this._cacheFull = false;\n if (!wasPaused) {\n this.resume();\n } else {\n this._notify(true);\n }\n }\n }\n\n /**\n * Get an attachment record by ID.\n */\n async getRecord(id: string): Promise<AttachmentRecord | null> {\n return this.powersync.get<AttachmentRecord>(`SELECT * FROM ${this.tableName} WHERE id = ?`, [id]);\n }\n\n /**\n * Get the local file URI for an attachment.\n */\n getLocalUri(localPath: string): string {\n const cacheDir = this.platform.fileSystem.getCacheDirectory();\n return `${cacheDir}attachments/${localPath}`;\n }\n\n /**\n * Cache a local file (e.g. one just uploaded) into the attachment cache.\n * This avoids a redundant download by copying the source file directly\n * into the cache directory and marking it as SYNCED.\n */\n async cacheLocalFile(storagePath: string, sourceUri: string): Promise<void> {\n if (!this._initialized) {\n this.logger.warn('[AttachmentQueue] cacheLocalFile called before init');\n return;\n }\n try {\n // Verify source file exists (temp files can be cleaned by OS)\n const sourceInfo = await this.platform.fileSystem.getFileInfo(sourceUri);\n if (!sourceInfo || !sourceInfo.exists) {\n this.logger.warn(`[AttachmentQueue] Source file does not exist: ${sourceUri}`);\n return;\n }\n\n // Same path sanitization as _downloadOne\n const localPath = storagePath.replace(/[^a-zA-Z0-9]/g, '_');\n const localUri = this.getLocalUri(localPath);\n\n // Ensure cache directory exists\n const dir = localUri.substring(0, localUri.lastIndexOf('/'));\n await this.platform.fileSystem.makeDirectory(dir, {\n intermediates: true\n });\n\n // Copy uploaded file into cache\n await this.platform.fileSystem.copyFile(sourceUri, localUri);\n\n // Get file size for cache tracking\n const info = await this.platform.fileSystem.getFileInfo(localUri);\n const size = info && info.exists ? info.size : 0;\n\n // Infer media type from extension\n const ext = storagePath.split('.').pop()?.toLowerCase() ?? '';\n const mediaTypeMap: Record<string, string> = {\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n png: 'image/png',\n webp: 'image/webp',\n heic: 'image/heic',\n heif: 'image/heif',\n gif: 'image/gif',\n mp4: 'video/mp4',\n mov: 'video/quicktime'\n };\n const mediaType = mediaTypeMap[ext] ?? 'application/octet-stream';\n\n // INSERT OR REPLACE as SYNCED\n await this.powersync.execute(`INSERT OR REPLACE INTO ${this.tableName} (id, filename, media_type, state, local_uri, size, timestamp)\n VALUES (?, ?, ?, ?, ?, ?, ?)`, [storagePath, storagePath, mediaType, AttachmentState.SYNCED, localPath, size, Date.now()]);\n\n // Invalidate stats caches\n this._cachedStats = null;\n this._cachedStatsTimestamp = 0;\n this._cachedSizeTimestamp = 0;\n\n // Notify subscribers\n this._notify(true);\n this.logger.info(`[AttachmentQueue] Cached local file: ${storagePath} (${Math.round(size / 1024)}KB)`);\n } catch (err) {\n this.logger.warn(`[AttachmentQueue] Failed to cache local file: ${storagePath}`, err);\n }\n }\n\n // ─── Download Watcher ──────────────────────────────────────────────────────\n\n private _startDownloadWatcher(): void {\n const {\n table,\n idColumn,\n projectFilter\n } = this.config.source;\n let query: string;\n if (projectFilter) {\n const {\n foreignKey,\n intermediaryTable,\n projectForeignKey\n } = projectFilter;\n query = `\n SELECT m.${idColumn} as id\n FROM ${table} m\n JOIN ${intermediaryTable} u ON m.${foreignKey} = u.id\n JOIN ProjectDatabase p ON u.${projectForeignKey} = p.id\n WHERE m.${idColumn} IS NOT NULL AND m.${idColumn} != ''\n `;\n } else {\n query = `SELECT ${idColumn} as id FROM ${table}\n WHERE ${idColumn} IS NOT NULL AND ${idColumn} != ''`;\n }\n\n // Use a simple interval-based approach since we can't rely on powersync.watch\n const checkForNewAttachments = async () => {\n // Check if disposed or aborted before executing\n if (this._abort.signal.aborted || !this._initialized) {\n return;\n }\n try {\n const result = await this.powersync.getAll<IdRow>(query);\n // Handle potential null/undefined result (defensive)\n const ids = (result ?? []).map(r => r.id);\n\n // No attachments found - this is normal for new projects\n if (ids.length === 0) {\n return;\n }\n\n // Sync new attachments to queue\n await this._syncAttachmentIds(ids);\n\n // Start downloads if not paused\n if (!this._paused && !this._processing) {\n this._startDownloads();\n }\n } catch (err) {\n // Handle expected conditions gracefully (new projects, initial sync)\n const errorMessage = String(err);\n if (errorMessage.includes('Result set is empty') || errorMessage.includes('no such table') || errorMessage.includes('SQLITE_EMPTY')) {\n // This is expected during initial sync or for new projects - log at debug level\n this.logger.debug('[AttachmentQueue] No attachments found in source table (expected for new projects)');\n return;\n }\n this.logger.warn('[AttachmentQueue] Watch error:', err);\n }\n };\n\n // Initial check\n checkForNewAttachments();\n\n // Periodic check every 30 seconds - store reference for cleanup\n this._watcherInterval = setInterval(checkForNewAttachments, 30000);\n }\n private async _syncAttachmentIds(ids: string[]): Promise<void> {\n for (const id of ids) {\n const existing = await this.getRecord(id);\n if (!existing) {\n await this.powersync.execute(`INSERT OR IGNORE INTO ${this.tableName} (id, filename, state, timestamp)\n VALUES (?, ?, ?, ?)`, [id, id, AttachmentState.QUEUED_DOWNLOAD, Date.now()]);\n }\n }\n }\n\n // ─── Download Engine ───────────────────────────────────────────────────────\n\n private async _startDownloads(): Promise<void> {\n if (this._paused || this._processing) {\n if (this._processing) {\n this._resumeRequested = true;\n }\n return;\n }\n this.logger.info('[AttachmentQueue] Starting downloads');\n this._processing = true;\n this._resumeRequested = false;\n const signal = this._abort.signal;\n try {\n let toProcess = await this._getIdsToDownload();\n let prevQueueSize = 0;\n while (toProcess.length > 0 && !signal.aborted) {\n const ordered = await this._orderByNewest(toProcess);\n let currentCachedSize = await this._getCachedSizeWithCache();\n const downloadStopLimit = this._maxCacheSize * this.downloadStopThreshold;\n if (currentCachedSize >= downloadStopLimit) {\n this._cacheFull = true;\n this.logger.info('[AttachmentQueue] Cache at capacity, stopping downloads');\n break;\n }\n this._cacheFull = false;\n for (let i = 0; i < ordered.length; i += this.concurrency) {\n if (signal.aborted) break;\n if (currentCachedSize >= downloadStopLimit) {\n this._cacheFull = true;\n break;\n }\n const chunk = ordered.slice(i, i + this.concurrency);\n const results = await Promise.allSettled(chunk.map(id => this._downloadOne(id, signal)));\n const successful: DownloadResult[] = [];\n for (const result of results) {\n if (result.status === 'fulfilled' && result.value) {\n successful.push(result.value);\n }\n }\n if (successful.length > 0) {\n await this._batchUpdateSizes(successful);\n currentCachedSize = await this._getCachedSizeWithCache(true);\n }\n }\n if (signal.aborted) break;\n toProcess = await this._getIdsToDownload();\n if (toProcess.length > 0 && toProcess.length >= prevQueueSize) {\n await this._sleep(this.retryDelayMs, signal);\n }\n prevQueueSize = toProcess.length;\n }\n } catch (err) {\n this.logger.error('[AttachmentQueue] Download loop error:', err);\n } finally {\n this._processing = false;\n this._notify(true);\n if (this._resumeRequested && !this._paused) {\n this._resumeRequested = false;\n this._startDownloads();\n }\n }\n }\n private async _downloadOne(id: string, signal: AbortSignal): Promise<DownloadResult | null> {\n if (signal.aborted) return null;\n const record = await this.getRecord(id);\n if (!record || signal.aborted) return null;\n this._downloads.set(id, {\n id,\n filename: record.filename,\n phase: 'downloading'\n });\n try {\n // Download from storage\n const data = await this._withTimeout(this.config.storage.downloadFile(record.filename), this.downloadTimeoutMs, `Download timeout: ${record.filename}`);\n if (signal.aborted) return null;\n\n // Save to local file\n const localPath = `${id.replace(/[^a-zA-Z0-9]/g, '_')}`;\n const localUri = this.getLocalUri(localPath);\n\n // Ensure directory exists\n const dir = localUri.substring(0, localUri.lastIndexOf('/'));\n await this.platform.fileSystem.makeDirectory(dir, {\n intermediates: true\n });\n\n // Write file\n const content = data instanceof Blob ? await this._blobToBase64(data) : data;\n await this.platform.fileSystem.writeFile(localUri, content, 'base64');\n\n // Compress if applicable\n await this._compressImage(localUri, id, record.filename);\n\n // Get final size\n const info = await this.platform.fileSystem.getFileInfo(localUri);\n if (info && info.exists) {\n // Update record\n await this.powersync.execute(`UPDATE ${this.tableName}\n SET state = ?, local_uri = ?, size = ?\n WHERE id = ?`, [AttachmentState.SYNCED, localPath, info.size, id]);\n return {\n id,\n size: info.size\n };\n }\n return null;\n } catch (err) {\n this.logger.warn(`[AttachmentQueue] Failed: ${record.filename}`, err);\n return null;\n } finally {\n this._downloads.delete(id);\n this._notify();\n }\n }\n private async _batchUpdateSizes(results: DownloadResult[]): Promise<void> {\n if (results.length === 0) return;\n for (const {\n id,\n size\n } of results) {\n await this.powersync.execute(`UPDATE ${this.tableName} SET size = ? WHERE id = ? AND state = ?`, [size, id, AttachmentState.SYNCED]);\n }\n this._cachedSizeTimestamp = 0;\n }\n private async _getIdsToDownload(): Promise<string[]> {\n const result = await this.powersync.getAll<IdRow>(`SELECT id FROM ${this.tableName}\n WHERE state IN (?, ?)`, [AttachmentState.QUEUED_DOWNLOAD, AttachmentState.QUEUED_SYNC]);\n return result.map(r => r.id);\n }\n\n // ─── Eviction ──────────────────────────────────────────────────────────────\n\n private async _evictIfNeeded(): Promise<void> {\n const evictionTrigger = this._maxCacheSize * this.evictionTriggerThreshold;\n const targetSize = this._maxCacheSize * this.downloadStopThreshold;\n let currentSize = await this._getCachedSizeWithCache(true);\n if (currentSize <= evictionTrigger) return;\n this.logger.info('[AttachmentQueue] Starting eviction...');\n while (currentSize > targetSize) {\n const toEvict = await this.powersync.getAll<EvictRow>(`SELECT id, local_uri, size FROM ${this.tableName}\n WHERE state = ${AttachmentState.SYNCED} AND size > 0 LIMIT 100`);\n if (toEvict.length === 0) break;\n for (const row of toEvict) {\n if (currentSize <= targetSize) break;\n if (row.local_uri) {\n try {\n await this.platform.fileSystem.deleteFile(this.getLocalUri(row.local_uri));\n await this.powersync.execute(`UPDATE ${this.tableName}\n SET state = ?, local_uri = NULL, size = 0 WHERE id = ?`, [AttachmentState.QUEUED_DOWNLOAD, row.id]);\n currentSize -= row.size;\n } catch {\n // Skip failed deletions\n }\n }\n }\n this._cachedSizeTimestamp = 0;\n currentSize = await this._getCachedSizeWithCache(true);\n }\n this._cacheFull = currentSize > targetSize;\n this._notify(true);\n if (!this._cacheFull && !this._paused && !this._processing) {\n this._startDownloads();\n }\n }\n\n // ─── Compression ───────────────────────────────────────────────────────────\n\n private async _compressImage(localUri: string, id: string, filename: string): Promise<void> {\n if (!this._isImage(filename)) return;\n if (!this.platform.imageProcessor) return;\n const compressionConfig = {\n ...DEFAULT_COMPRESSION_CONFIG,\n ...this.config.compression\n };\n if (!compressionConfig.enabled) return;\n try {\n const info = await this.platform.fileSystem.getFileInfo(localUri);\n if (!info || !info.exists) return;\n const originalSize = info.size;\n if (originalSize < compressionConfig.skipSizeBytes) return;\n if (originalSize < compressionConfig.targetSizeBytes) return;\n\n // Update phase\n const existing = this._downloads.get(id);\n if (existing) {\n this._downloads.set(id, {\n ...existing,\n phase: 'compressing'\n });\n this._notify(false);\n }\n const result = await this.platform.imageProcessor.compress(localUri, {\n quality: this._compressionQuality,\n maxWidth: originalSize > 1_000_000 ? compressionConfig.maxWidth : undefined,\n format: 'jpeg'\n });\n const compressedInfo = await this.platform.fileSystem.getFileInfo(result.uri);\n if (!compressedInfo || !compressedInfo.exists) return;\n if (compressedInfo.size >= originalSize) {\n await this.platform.fileSystem.deleteFile(result.uri);\n return;\n }\n\n // Replace original with compressed\n await this.platform.fileSystem.deleteFile(localUri);\n await this.platform.fileSystem.moveFile(result.uri, localUri);\n this.logger.info(`[AttachmentQueue] Compressed ${filename}: ${Math.round(originalSize / 1024)}KB → ${Math.round(compressedInfo.size / 1024)}KB`);\n } catch (err) {\n this.logger.warn(`[AttachmentQueue] Compression failed for ${filename}:`, err);\n }\n }\n\n // ─── Helpers ───────────────────────────────────────────────────────────────\n\n private _isImage(filename: string): boolean {\n const ext = filename.split('.').pop()?.toLowerCase();\n return ['jpg', 'jpeg', 'png', 'webp', 'heic', 'heif'].includes(ext ?? '');\n }\n private _getStatus(pendingCount: number): AttachmentSyncStatus {\n if (this._processing) return 'syncing';\n if (this._paused) return 'paused';\n if (this._cacheFull && pendingCount > 0) return 'cache_full';\n return 'complete';\n }\n private async _getCachedSizeWithCache(forceRefresh = false): Promise<number> {\n const now = Date.now();\n if (!forceRefresh && this._cachedSizeTimestamp > 0 && now - this._cachedSizeTimestamp < CACHE_SIZE_TTL_MS) {\n return this._cachedSize;\n }\n const result = await this.powersync.get<CachedSizeRow>(`SELECT COALESCE(SUM(size), 0) as total FROM ${this.tableName}\n WHERE state = ${AttachmentState.SYNCED}`);\n this._cachedSize = result?.total ?? 0;\n this._cachedSizeTimestamp = now;\n return this._cachedSize;\n }\n private async _orderByNewest(ids: string[]): Promise<string[]> {\n if (ids.length <= 3) return ids;\n const {\n table,\n idColumn,\n orderByColumn\n } = this.config.source;\n if (!orderByColumn) return ids;\n try {\n const idSet = new Set(ids);\n const ordered = await this.powersync.getAll<{\n id: string;\n }>(`SELECT ${idColumn} as id FROM ${table}\n WHERE ${idColumn} IS NOT NULL AND ${idColumn} != ''\n ORDER BY ${orderByColumn} DESC NULLS LAST\n LIMIT ?`, [ids.length * 2]);\n const result: string[] = [];\n for (const row of ordered) {\n if (idSet.has(row.id)) {\n result.push(row.id);\n idSet.delete(row.id);\n }\n }\n for (const id of ids) {\n if (idSet.has(id)) {\n result.push(id);\n }\n }\n return result;\n } catch {\n return ids;\n }\n }\n private _sleep(ms: number, signal: AbortSignal): Promise<void> {\n return new Promise(resolve => {\n const onAbort = () => {\n clearTimeout(timer);\n resolve();\n };\n const timer = setTimeout(() => {\n signal.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n signal.addEventListener('abort', onAbort, {\n once: true\n });\n });\n }\n private _withTimeout<T>(promise: Promise<T>, ms: number, message: string): Promise<T> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => reject(new Error(message)), ms);\n promise.then(result => {\n clearTimeout(timer);\n resolve(result);\n }, err => {\n clearTimeout(timer);\n reject(err);\n });\n });\n }\n private async _blobToBase64(blob: Blob): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onloadend = () => {\n const base64 = (reader.result as string).split(',')[1];\n resolve(base64);\n };\n reader.onerror = reject;\n reader.readAsDataURL(blob);\n });\n }\n private _notify(forceImmediate = false): void {\n if (this._progressCallbacks.size === 0) return;\n const now = Date.now();\n const timeSinceLastNotify = now - this._lastNotifyTime;\n if (this._notifyTimer) {\n clearTimeout(this._notifyTimer);\n this._notifyTimer = null;\n }\n const notifyAll = (stats: AttachmentSyncStats) => {\n for (const cb of this._progressCallbacks) {\n try {\n cb(stats);\n } catch (err) {\n this.logger.warn('[AttachmentQueue] Callback error:', err);\n }\n }\n };\n if (forceImmediate || timeSinceLastNotify >= NOTIFY_THROTTLE_MS) {\n this._lastNotifyTime = now;\n this.getStats().then(notifyAll).catch(() => {});\n } else {\n const delay = NOTIFY_THROTTLE_MS - timeSinceLastNotify;\n this._notifyTimer = setTimeout(() => {\n this._notifyTimer = null;\n this._lastNotifyTime = Date.now();\n this.getStats().then(notifyAll).catch(() => {});\n }, delay);\n }\n }\n}"],"mappings":"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","names":["AttachmentState"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { e as ConflictBus, C as ConnectorConfig, c as CrudHandler, P as PowerSyncCredentials, b as SchemaRouter, S as SupabaseConnector, a as SupabaseConnectorOptions, d as defaultSchemaRouter } from '../index-
|
|
1
|
+
export { e as ConflictBus, C as ConnectorConfig, c as CrudHandler, P as PowerSyncCredentials, b as SchemaRouter, S as SupabaseConnector, a as SupabaseConnectorOptions, d as defaultSchemaRouter } from '../index-Cb-NI0Ct.js';
|
|
2
2
|
export { P as PowerSyncBackendConnector } from '../types-afHtE1U_.js';
|
|
3
3
|
import '@supabase/supabase-js';
|
|
4
4
|
import '../platform/index.js';
|
package/dist/connector/index.js
CHANGED
|
@@ -2,8 +2,8 @@ import "../chunk-VJCL2SWD.js";
|
|
|
2
2
|
import {
|
|
3
3
|
SupabaseConnector,
|
|
4
4
|
defaultSchemaRouter
|
|
5
|
-
} from "../chunk-
|
|
6
|
-
import "../chunk-
|
|
5
|
+
} from "../chunk-C2RSTGDC.js";
|
|
6
|
+
import "../chunk-FPTDATY5.js";
|
|
7
7
|
export {
|
|
8
8
|
SupabaseConnector,
|
|
9
9
|
defaultSchemaRouter
|
package/dist/core/index.js
CHANGED
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
STORAGE_KEY_PREFIX,
|
|
31
31
|
STORAGE_KEY_SYNC_MODE,
|
|
32
32
|
STORAGE_WARNING_THRESHOLD
|
|
33
|
-
} from "../chunk-
|
|
33
|
+
} from "../chunk-EJ23MXPQ.js";
|
|
34
34
|
import {
|
|
35
35
|
AttachmentError,
|
|
36
36
|
ConfigurationError,
|
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
extractTableNames,
|
|
47
47
|
generateFailureId,
|
|
48
48
|
toSyncOperationError
|
|
49
|
-
} from "../chunk-
|
|
49
|
+
} from "../chunk-FPTDATY5.js";
|
|
50
50
|
export {
|
|
51
51
|
ATTACHMENT_DOWNLOAD_TIMEOUT_MS,
|
|
52
52
|
ATTACHMENT_RETRY_DELAY_MS,
|
|
@@ -74,7 +74,7 @@ interface ConflictDetectionConfig {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
type ConflictListener = (conflict: ConflictCheckResult) => void;
|
|
77
|
-
type ResolutionListener = (recordId: string, resolution: ConflictResolution) => void;
|
|
77
|
+
type ResolutionListener = (table: string, recordId: string, resolution: ConflictResolution) => void;
|
|
78
78
|
/**
|
|
79
79
|
* Event bus for decoupling conflict detection from UI resolution.
|
|
80
80
|
*
|
|
@@ -104,7 +104,7 @@ declare class ConflictBus {
|
|
|
104
104
|
/**
|
|
105
105
|
* Emit a resolution event (called by UI/ConflictContext).
|
|
106
106
|
*/
|
|
107
|
-
emitResolution(recordId: string, resolution: ConflictResolution): void;
|
|
107
|
+
emitResolution(table: string, recordId: string, resolution: ConflictResolution): void;
|
|
108
108
|
/**
|
|
109
109
|
* Clear all listeners (for cleanup).
|
|
110
110
|
*/
|
|
@@ -321,7 +321,14 @@ declare class SupabaseConnector implements PowerSyncBackendConnector {
|
|
|
321
321
|
private readonly conflictBus?;
|
|
322
322
|
private versionColumnCache;
|
|
323
323
|
private activeProjectIds;
|
|
324
|
+
private resolvedConflicts;
|
|
325
|
+
private unsubscribeResolution?;
|
|
324
326
|
constructor(options: SupabaseConnectorOptions);
|
|
327
|
+
/**
|
|
328
|
+
* Clean up resources (unsubscribe from event listeners).
|
|
329
|
+
* Call this when the connector is no longer needed.
|
|
330
|
+
*/
|
|
331
|
+
destroy(): void;
|
|
325
332
|
/**
|
|
326
333
|
* Set the active project IDs for scoped sync.
|
|
327
334
|
* Call this when user selects/opens projects.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { A as AbstractPowerSyncDatabase } from './types-afHtE1U_.js';
|
|
2
2
|
export { k as CacheStats, i as ClassifiedError, j as CompactResult, h as CompletedTransaction, b as ConnectionHealth, o as CountRow, C as CrudEntry, l as CrudTransaction, n as DbStatRow, D as DownloadProgress, E as EntitySyncState, F as FailedTransaction, r as FreelistCountRow, s as IntegrityCheckRow, I as IntegrityResult, q as PageCountRow, p as PageSizeRow, P as PowerSyncBackendConnector, m as SqliteTableRow, c as StorageInfo, d as StorageQuota, f as SyncError, g as SyncErrorType, e as SyncMetrics, S as SyncMode, a as SyncStatus, T as TableCacheStats } from './types-afHtE1U_.js';
|
|
3
3
|
export { ATTACHMENT_DOWNLOAD_TIMEOUT_MS, ATTACHMENT_RETRY_DELAY_MS, AttachmentError, COMPRESSION_MAX_WIDTH, COMPRESSION_SKIP_SIZE_BYTES, COMPRESSION_TARGET_SIZE_BYTES, ConfigurationError, ConnectorError, DEFAULT_ATTACHMENT_CACHE_SIZE, DEFAULT_ATTACHMENT_CONCURRENCY, DEFAULT_COMPRESSION_QUALITY, DEFAULT_MAX_RETRY_ATTEMPTS, DEFAULT_RETRY_BACKOFF_MULTIPLIER, DEFAULT_RETRY_BASE_DELAY_MS, DEFAULT_RETRY_MAX_DELAY_MS, DEFAULT_SYNC_INTERVAL_MS, DEFAULT_SYNC_MODE, DOWNLOAD_STOP_THRESHOLD, EVICTION_TRIGGER_THRESHOLD, HEALTH_CHECK_INTERVAL_MS, HEALTH_CHECK_TIMEOUT_MS, InitializationError, LATENCY_DEGRADED_THRESHOLD_MS, MAX_CONSECUTIVE_FAILURES, PlatformAdapterError, PowerSyncError, STATS_CACHE_TTL_MS, STATUS_NOTIFY_THROTTLE_MS, STORAGE_CRITICAL_THRESHOLD, STORAGE_KEY_ATTACHMENT_SETTINGS, STORAGE_KEY_ENABLED, STORAGE_KEY_METRICS, STORAGE_KEY_PAUSED, STORAGE_KEY_PREFIX, STORAGE_KEY_SYNC_MODE, STORAGE_WARNING_THRESHOLD, SyncOperationError, classifyError, classifySupabaseError, createSyncError, extractEntityIds, extractTableNames, generateFailureId, toSyncOperationError } from './core/index.js';
|
|
4
|
-
import { i as ConflictDetectionConfig, f as ConflictCheckResult } from './index-
|
|
5
|
-
export { e as ConflictBus, h as ConflictHandler, j as ConflictListener, g as ConflictResolution, C as ConnectorConfig, c as CrudHandler, F as FieldConflict, P as PowerSyncCredentials, R as ResolutionListener, b as SchemaRouter, S as SupabaseConnector, a as SupabaseConnectorOptions, d as defaultSchemaRouter } from './index-
|
|
4
|
+
import { i as ConflictDetectionConfig, f as ConflictCheckResult } from './index-Cb-NI0Ct.js';
|
|
5
|
+
export { e as ConflictBus, h as ConflictHandler, j as ConflictListener, g as ConflictResolution, C as ConnectorConfig, c as CrudHandler, F as FieldConflict, P as PowerSyncCredentials, R as ResolutionListener, b as SchemaRouter, S as SupabaseConnector, a as SupabaseConnectorOptions, d as defaultSchemaRouter } from './index-Cb-NI0Ct.js';
|
|
6
6
|
export { AttachmentQueue, AttachmentQueueConfig, AttachmentRecord, AttachmentSourceConfig, AttachmentState, AttachmentStatsRow, AttachmentStorageAdapter, AttachmentSyncStats, AttachmentSyncStatus, CacheConfig, CacheFileRow, CachedSizeRow, CompressionConfig, DEFAULT_CACHE_CONFIG, DEFAULT_COMPRESSION_CONFIG, DEFAULT_DOWNLOAD_CONFIG, DownloadConfig, DownloadPhase, DownloadStatus, EvictRow, IdRow } from './attachments/index.js';
|
|
7
7
|
export { e as HealthCheckResult, H as HealthMonitorOptions, M as MetricsCollectorOptions, P as PowerSyncRawStatus, c as SyncControlActions, f as SyncEvent, g as SyncEventListener, d as SyncOperationData, S as SyncScope, a as SyncStatusState, b as SyncStatusTrackerOptions, U as Unsubscribe } from './types-Cd7RhNqf.js';
|
|
8
8
|
export { HealthMonitor, MetricsCollector, SyncStatusTracker } from './sync/index.js';
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
ConflictBus
|
|
3
|
-
} from "./chunk-42IJ25Q4.js";
|
|
1
|
+
import "./chunk-OLHGI472.js";
|
|
4
2
|
import "./chunk-32OLICZO.js";
|
|
5
3
|
import {
|
|
6
4
|
AttachmentQueueContext,
|
|
5
|
+
ConflictBus,
|
|
7
6
|
ConnectionHealthContext,
|
|
8
7
|
PowerSyncContext,
|
|
9
8
|
PowerSyncProvider,
|
|
@@ -25,14 +24,14 @@ import {
|
|
|
25
24
|
useSyncMode,
|
|
26
25
|
useSyncStatus,
|
|
27
26
|
useUploadStatus
|
|
28
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-GMFDCVMZ.js";
|
|
29
28
|
import {
|
|
30
29
|
AttachmentQueue,
|
|
31
30
|
AttachmentState,
|
|
32
31
|
DEFAULT_CACHE_CONFIG,
|
|
33
32
|
DEFAULT_COMPRESSION_CONFIG,
|
|
34
33
|
DEFAULT_DOWNLOAD_CONFIG
|
|
35
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-V6LJ6MR2.js";
|
|
36
35
|
import "./chunk-7JQZBZ5N.js";
|
|
37
36
|
import {
|
|
38
37
|
DEFAULT_CONNECTION_HEALTH,
|
|
@@ -42,7 +41,7 @@ import {
|
|
|
42
41
|
HealthMonitor,
|
|
43
42
|
MetricsCollector,
|
|
44
43
|
SyncStatusTracker
|
|
45
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-OTJXIRWX.js";
|
|
46
45
|
import "./chunk-W7HSR35B.js";
|
|
47
46
|
import {
|
|
48
47
|
ATTACHMENT_DOWNLOAD_TIMEOUT_MS,
|
|
@@ -75,7 +74,7 @@ import {
|
|
|
75
74
|
STORAGE_KEY_PREFIX,
|
|
76
75
|
STORAGE_KEY_SYNC_MODE,
|
|
77
76
|
STORAGE_WARNING_THRESHOLD
|
|
78
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-EJ23MXPQ.js";
|
|
79
78
|
import "./chunk-VJCL2SWD.js";
|
|
80
79
|
import {
|
|
81
80
|
SupabaseConnector,
|
|
@@ -84,7 +83,7 @@ import {
|
|
|
84
83
|
fetchServerVersion,
|
|
85
84
|
getLocalVersion,
|
|
86
85
|
hasVersionColumn
|
|
87
|
-
} from "./chunk-
|
|
86
|
+
} from "./chunk-C2RSTGDC.js";
|
|
88
87
|
import {
|
|
89
88
|
AttachmentError,
|
|
90
89
|
ConfigurationError,
|
|
@@ -100,7 +99,7 @@ import {
|
|
|
100
99
|
extractTableNames,
|
|
101
100
|
generateFailureId,
|
|
102
101
|
toSyncOperationError
|
|
103
|
-
} from "./chunk-
|
|
102
|
+
} from "./chunk-FPTDATY5.js";
|
|
104
103
|
export {
|
|
105
104
|
ATTACHMENT_DOWNLOAD_TIMEOUT_MS,
|
|
106
105
|
ATTACHMENT_RETRY_DELAY_MS,
|