@powersync/common 1.57.3 → 2.1.0
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/lib/attachments/AttachmentContext.d.ts +5 -5
- package/lib/attachments/AttachmentContext.js +6 -2
- package/lib/attachments/AttachmentContext.js.map +1 -1
- package/lib/attachments/AttachmentQueue.d.ts +85 -47
- package/lib/attachments/AttachmentQueue.js +55 -18
- package/lib/attachments/AttachmentQueue.js.map +1 -1
- package/lib/attachments/AttachmentService.d.ts +3 -3
- package/lib/attachments/AttachmentService.js +4 -3
- package/lib/attachments/AttachmentService.js.map +1 -1
- package/lib/attachments/AttachmentTransportAdapter.d.ts +47 -0
- package/lib/attachments/AttachmentTransportAdapter.js +2 -0
- package/lib/attachments/AttachmentTransportAdapter.js.map +1 -0
- package/lib/attachments/BufferedAttachmentTransport.d.ts +24 -0
- package/lib/attachments/BufferedAttachmentTransport.js +32 -0
- package/lib/attachments/BufferedAttachmentTransport.js.map +1 -0
- package/lib/attachments/LocalStorageAdapter.d.ts +19 -0
- package/lib/attachments/Schema.d.ts +2 -2
- package/lib/attachments/SyncingService.d.ts +8 -5
- package/lib/attachments/SyncingService.js +31 -15
- package/lib/attachments/SyncingService.js.map +1 -1
- package/lib/client/{AbstractPowerSyncDatabase.d.ts → CommonPowerSyncDatabase.d.ts} +56 -232
- package/lib/client/CommonPowerSyncDatabase.js +2 -0
- package/lib/client/CommonPowerSyncDatabase.js.map +1 -0
- package/lib/client/SQLOpenFactory.d.ts +35 -13
- package/lib/client/SQLOpenFactory.js +1 -25
- package/lib/client/SQLOpenFactory.js.map +1 -1
- package/lib/client/compilableQueryWatch.d.ts +2 -2
- package/lib/client/compilableQueryWatch.js +1 -2
- package/lib/client/compilableQueryWatch.js.map +1 -1
- package/lib/client/connection/PowerSyncBackendConnector.d.ts +3 -3
- package/lib/client/runOnSchemaChange.d.ts +2 -2
- package/lib/client/runOnSchemaChange.js.map +1 -1
- package/lib/client/sync/bucket/CrudEntry.d.ts +3 -34
- package/lib/client/sync/bucket/CrudEntry.js +0 -99
- package/lib/client/sync/bucket/CrudEntry.js.map +1 -1
- package/lib/client/sync/options.d.ts +65 -0
- package/lib/client/sync/options.js +25 -0
- package/lib/client/sync/options.js.map +1 -0
- package/lib/client/sync/sync-streams.d.ts +1 -1
- package/lib/client/triggers/TriggerManager.d.ts +9 -38
- package/lib/client/watched/GetAllQuery.d.ts +3 -3
- package/lib/client/watched/GetAllQuery.js +1 -1
- package/lib/client/watched/GetAllQuery.js.map +1 -1
- package/lib/client/watched/WatchedQuery.d.ts +2 -10
- package/lib/client/watched/WatchedQuery.js +0 -11
- package/lib/client/watched/WatchedQuery.js.map +1 -1
- package/lib/client/watched/processors/DifferentialQueryProcessor.d.ts +0 -50
- package/lib/client/watched/processors/DifferentialQueryProcessor.js +1 -176
- package/lib/client/watched/processors/DifferentialQueryProcessor.js.map +1 -1
- package/lib/client/watched/processors/OnChangeQueryProcessor.d.ts +0 -22
- package/lib/client/watched/processors/OnChangeQueryProcessor.js +1 -82
- package/lib/client/watched/processors/OnChangeQueryProcessor.js.map +1 -1
- package/lib/db/DBAdapter.d.ts +72 -154
- package/lib/db/DBAdapter.js +70 -108
- package/lib/db/DBAdapter.js.map +1 -1
- package/lib/db/QueryResult.d.ts +104 -0
- package/lib/db/QueryResult.js +96 -0
- package/lib/db/QueryResult.js.map +1 -0
- package/lib/db/crud/SyncProgress.d.ts +1 -13
- package/lib/db/crud/SyncProgress.js +1 -62
- package/lib/db/crud/SyncProgress.js.map +1 -1
- package/lib/db/crud/SyncStatus.d.ts +32 -66
- package/lib/db/crud/SyncStatus.js +1 -253
- package/lib/db/crud/SyncStatus.js.map +1 -1
- package/lib/db/schema/Column.d.ts +0 -7
- package/lib/db/schema/Column.js +0 -7
- package/lib/db/schema/Column.js.map +1 -1
- package/lib/db/schema/Index.d.ts +2 -6
- package/lib/db/schema/Index.js +1 -4
- package/lib/db/schema/Index.js.map +1 -1
- package/lib/db/schema/IndexedColumn.d.ts +2 -6
- package/lib/db/schema/IndexedColumn.js +1 -4
- package/lib/db/schema/IndexedColumn.js.map +1 -1
- package/lib/db/schema/Schema.d.ts +4 -28
- package/lib/db/schema/Schema.js.map +1 -1
- package/lib/db/schema/Table.d.ts +50 -96
- package/lib/db/schema/Table.js +83 -91
- package/lib/db/schema/Table.js.map +1 -1
- package/lib/index.d.ts +6 -21
- package/lib/index.js +6 -22
- package/lib/index.js.map +1 -1
- package/lib/utils/BaseObserver.d.ts +1 -1
- package/lib/utils/BaseObserver.js +1 -1
- package/lib/utils/Logger.d.ts +52 -23
- package/lib/utils/Logger.js +35 -33
- package/lib/utils/Logger.js.map +1 -1
- package/lib/utils/MetaBaseObserver.d.ts +11 -11
- package/lib/utils/MetaBaseObserver.js +1 -50
- package/lib/utils/MetaBaseObserver.js.map +1 -1
- package/lib/utils/mutex.d.ts +3 -53
- package/lib/utils/mutex.js +1 -156
- package/lib/utils/mutex.js.map +1 -1
- package/package.json +8 -45
- package/src/attachments/AttachmentContext.ts +11 -10
- package/src/attachments/AttachmentQueue.ts +129 -70
- package/src/attachments/AttachmentService.ts +7 -6
- package/src/attachments/AttachmentTransportAdapter.ts +49 -0
- package/src/attachments/BufferedAttachmentTransport.ts +37 -0
- package/src/attachments/LocalStorageAdapter.ts +20 -0
- package/src/attachments/Schema.ts +2 -2
- package/src/attachments/SyncingService.ts +34 -20
- package/src/client/CommonPowerSyncDatabase.ts +540 -0
- package/src/client/SQLOpenFactory.ts +38 -22
- package/src/client/compilableQueryWatch.ts +3 -4
- package/src/client/connection/PowerSyncBackendConnector.ts +3 -3
- package/src/client/runOnSchemaChange.ts +2 -2
- package/src/client/sync/bucket/CrudEntry.ts +4 -104
- package/src/client/sync/options.ts +77 -0
- package/src/client/sync/sync-streams.ts +1 -1
- package/src/client/triggers/TriggerManager.ts +10 -41
- package/src/client/watched/GetAllQuery.ts +3 -3
- package/src/client/watched/WatchedQuery.ts +2 -15
- package/src/client/watched/processors/DifferentialQueryProcessor.ts +0 -223
- package/src/client/watched/processors/OnChangeQueryProcessor.ts +0 -111
- package/src/db/DBAdapter.ts +141 -226
- package/src/db/QueryResult.ts +195 -0
- package/src/db/crud/SyncProgress.ts +2 -42
- package/src/db/crud/SyncStatus.ts +45 -220
- package/src/db/schema/Column.ts +0 -8
- package/src/db/schema/Index.ts +3 -6
- package/src/db/schema/IndexedColumn.ts +3 -6
- package/src/db/schema/Schema.ts +4 -4
- package/src/db/schema/Table.ts +111 -186
- package/src/index.ts +6 -21
- package/src/utils/BaseObserver.ts +1 -1
- package/src/utils/Logger.ts +80 -42
- package/src/utils/MetaBaseObserver.ts +14 -60
- package/src/utils/mutex.ts +4 -201
- package/dist/bundle.cjs +0 -14398
- package/dist/bundle.cjs.map +0 -1
- package/dist/bundle.mjs +0 -14317
- package/dist/bundle.mjs.map +0 -1
- package/dist/bundle.node.cjs +0 -12029
- package/dist/bundle.node.cjs.map +0 -1
- package/dist/bundle.node.mjs +0 -11948
- package/dist/bundle.node.mjs.map +0 -1
- package/dist/index.d.cts +0 -4651
- package/legacy/sync_protocol.d.ts +0 -103
- package/lib/client/AbstractPowerSyncDatabase.js +0 -990
- package/lib/client/AbstractPowerSyncDatabase.js.map +0 -1
- package/lib/client/AbstractPowerSyncOpenFactory.d.ts +0 -28
- package/lib/client/AbstractPowerSyncOpenFactory.js +0 -28
- package/lib/client/AbstractPowerSyncOpenFactory.js.map +0 -1
- package/lib/client/ConnectionManager.d.ts +0 -115
- package/lib/client/ConnectionManager.js +0 -294
- package/lib/client/ConnectionManager.js.map +0 -1
- package/lib/client/CustomQuery.d.ts +0 -22
- package/lib/client/CustomQuery.js +0 -43
- package/lib/client/CustomQuery.js.map +0 -1
- package/lib/client/constants.d.ts +0 -4
- package/lib/client/constants.js +0 -5
- package/lib/client/constants.js.map +0 -1
- package/lib/client/sync/bucket/BucketStorageAdapter.d.ts +0 -56
- package/lib/client/sync/bucket/BucketStorageAdapter.js +0 -29
- package/lib/client/sync/bucket/BucketStorageAdapter.js.map +0 -1
- package/lib/client/sync/bucket/SqliteBucketStorage.d.ts +0 -37
- package/lib/client/sync/bucket/SqliteBucketStorage.js +0 -165
- package/lib/client/sync/bucket/SqliteBucketStorage.js.map +0 -1
- package/lib/client/sync/stream/AbstractRemote.d.ts +0 -180
- package/lib/client/sync/stream/AbstractRemote.js +0 -508
- package/lib/client/sync/stream/AbstractRemote.js.map +0 -1
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.d.ts +0 -265
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js +0 -706
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js.map +0 -1
- package/lib/client/sync/stream/WebsocketClientTransport.d.ts +0 -15
- package/lib/client/sync/stream/WebsocketClientTransport.js +0 -62
- package/lib/client/sync/stream/WebsocketClientTransport.js.map +0 -1
- package/lib/client/sync/stream/core-instruction.d.ts +0 -77
- package/lib/client/sync/stream/core-instruction.js +0 -30
- package/lib/client/sync/stream/core-instruction.js.map +0 -1
- package/lib/client/triggers/MemoryTriggerClaimManager.d.ts +0 -6
- package/lib/client/triggers/MemoryTriggerClaimManager.js +0 -21
- package/lib/client/triggers/MemoryTriggerClaimManager.js.map +0 -1
- package/lib/client/triggers/TriggerManagerImpl.d.ts +0 -39
- package/lib/client/triggers/TriggerManagerImpl.js +0 -410
- package/lib/client/triggers/TriggerManagerImpl.js.map +0 -1
- package/lib/client/watched/processors/AbstractQueryProcessor.d.ts +0 -68
- package/lib/client/watched/processors/AbstractQueryProcessor.js +0 -151
- package/lib/client/watched/processors/AbstractQueryProcessor.js.map +0 -1
- package/lib/db/ConnectionClosedError.d.ts +0 -12
- package/lib/db/ConnectionClosedError.js +0 -23
- package/lib/db/ConnectionClosedError.js.map +0 -1
- package/lib/db/schema/TableV2.d.ts +0 -11
- package/lib/db/schema/TableV2.js +0 -11
- package/lib/db/schema/TableV2.js.map +0 -1
- package/lib/utils/AbortOperation.d.ts +0 -11
- package/lib/utils/AbortOperation.js +0 -21
- package/lib/utils/AbortOperation.js.map +0 -1
- package/lib/utils/ControlledExecutor.d.ts +0 -31
- package/lib/utils/ControlledExecutor.js +0 -58
- package/lib/utils/ControlledExecutor.js.map +0 -1
- package/lib/utils/async.d.ts +0 -46
- package/lib/utils/async.js +0 -147
- package/lib/utils/async.js.map +0 -1
- package/lib/utils/compatibility.d.ts +0 -8
- package/lib/utils/compatibility.js +0 -9
- package/lib/utils/compatibility.js.map +0 -1
- package/lib/utils/parseQuery.d.ts +0 -12
- package/lib/utils/parseQuery.js +0 -20
- package/lib/utils/parseQuery.js.map +0 -1
- package/lib/utils/queue.d.ts +0 -16
- package/lib/utils/queue.js +0 -42
- package/lib/utils/queue.js.map +0 -1
- package/lib/utils/stream_transform.d.ts +0 -41
- package/lib/utils/stream_transform.js +0 -211
- package/lib/utils/stream_transform.js.map +0 -1
- package/src/client/AbstractPowerSyncDatabase.ts +0 -1408
- package/src/client/AbstractPowerSyncOpenFactory.ts +0 -45
- package/src/client/ConnectionManager.ts +0 -405
- package/src/client/CustomQuery.ts +0 -56
- package/src/client/constants.ts +0 -4
- package/src/client/sync/bucket/BucketStorageAdapter.ts +0 -65
- package/src/client/sync/bucket/SqliteBucketStorage.ts +0 -202
- package/src/client/sync/stream/AbstractRemote.ts +0 -648
- package/src/client/sync/stream/AbstractStreamingSyncImplementation.ts +0 -971
- package/src/client/sync/stream/WebsocketClientTransport.ts +0 -82
- package/src/client/sync/stream/core-instruction.ts +0 -109
- package/src/client/triggers/MemoryTriggerClaimManager.ts +0 -25
- package/src/client/triggers/TriggerManagerImpl.ts +0 -501
- package/src/client/watched/processors/AbstractQueryProcessor.ts +0 -226
- package/src/db/ConnectionClosedError.ts +0 -25
- package/src/db/schema/TableV2.ts +0 -11
- package/src/utils/AbortOperation.ts +0 -19
- package/src/utils/ControlledExecutor.ts +0 -81
- package/src/utils/async.ts +0 -185
- package/src/utils/compatibility.ts +0 -9
- package/src/utils/parseQuery.ts +0 -31
- package/src/utils/queue.ts +0 -48
- package/src/utils/stream_transform.ts +0 -260
|
@@ -1,37 +1,36 @@
|
|
|
1
|
-
import { AbstractPowerSyncDatabase } from '../client/AbstractPowerSyncDatabase.js';
|
|
2
|
-
import { DEFAULT_WATCH_THROTTLE_MS } from '../client/watched/WatchedQuery.js';
|
|
3
1
|
import { DifferentialWatchedQuery } from '../client/watched/processors/DifferentialQueryProcessor.js';
|
|
4
2
|
import { Mutex } from '../utils/mutex.js';
|
|
3
|
+
import { LogLevels, PowerSyncLogger } from '../utils/Logger.js';
|
|
5
4
|
import { Transaction } from '../db/DBAdapter.js';
|
|
6
|
-
import { ILogger } from '../utils/Logger.js';
|
|
7
5
|
import { AttachmentContext } from './AttachmentContext.js';
|
|
8
6
|
import { AttachmentErrorHandler } from './AttachmentErrorHandler.js';
|
|
9
7
|
import { AttachmentService } from './AttachmentService.js';
|
|
10
|
-
import {
|
|
8
|
+
import { AttachmentTransportAdapter } from './AttachmentTransportAdapter.js';
|
|
9
|
+
import { BufferedAttachmentTransport } from './BufferedAttachmentTransport.js';
|
|
10
|
+
import { AttachmentData, LocalStorageAdapter, StreamingLocalStorageAdapter } from './LocalStorageAdapter.js';
|
|
11
11
|
import { RemoteStorageAdapter } from './RemoteStorageAdapter.js';
|
|
12
12
|
import { ATTACHMENT_TABLE, AttachmentRecord, AttachmentState } from './Schema.js';
|
|
13
13
|
import { SyncingService } from './SyncingService.js';
|
|
14
14
|
import { WatchedAttachmentItem } from './WatchedAttachmentItem.js';
|
|
15
|
+
import { CommonPowerSyncDatabase } from '../client/CommonPowerSyncDatabase.js';
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
|
-
*
|
|
18
|
+
* Fields common to every {@link AttachmentQueueOptions} variant.
|
|
18
19
|
*
|
|
19
20
|
* @experimental
|
|
20
21
|
* @alpha This is currently experimental and may change without a major version bump.
|
|
21
22
|
*/
|
|
22
|
-
export interface
|
|
23
|
+
export interface BaseAttachmentQueueOptions<TLocal extends LocalStorageAdapter = LocalStorageAdapter> {
|
|
23
24
|
/**
|
|
24
25
|
* PowerSync database instance
|
|
25
26
|
*/
|
|
26
|
-
db:
|
|
27
|
-
/**
|
|
28
|
-
* Remote storage adapter for upload/download operations
|
|
29
|
-
*/
|
|
30
|
-
remoteStorage: RemoteStorageAdapter;
|
|
27
|
+
db: CommonPowerSyncDatabase;
|
|
31
28
|
/**
|
|
32
|
-
* Local storage adapter for file persistence
|
|
29
|
+
* Local storage adapter for file persistence. Its type determines whether
|
|
30
|
+
* {@link AttachmentQueue.saveFileFromUri} is available (a
|
|
31
|
+
* {@link StreamingLocalStorageAdapter} enables it).
|
|
33
32
|
*/
|
|
34
|
-
localStorage:
|
|
33
|
+
localStorage: TLocal;
|
|
35
34
|
/**
|
|
36
35
|
* Callback for monitoring attachment changes in your data model
|
|
37
36
|
*/
|
|
@@ -43,7 +42,7 @@ export interface AttachmentQueueOptions {
|
|
|
43
42
|
/**
|
|
44
43
|
* Logger instance. Defaults to db.logger
|
|
45
44
|
*/
|
|
46
|
-
logger?:
|
|
45
|
+
logger?: PowerSyncLogger;
|
|
47
46
|
/**
|
|
48
47
|
* Periodic polling interval in milliseconds for retrying failed uploads/downloads. Default: 30000
|
|
49
48
|
*/
|
|
@@ -65,6 +64,52 @@ export interface AttachmentQueueOptions {
|
|
|
65
64
|
errorHandler?: AttachmentErrorHandler;
|
|
66
65
|
}
|
|
67
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Configuration options for {@link AttachmentQueue}.
|
|
69
|
+
*
|
|
70
|
+
* Provide **exactly one** remote mechanism:
|
|
71
|
+
* - `remoteStorage` — a {@link RemoteStorageAdapter}, wrapped in the default
|
|
72
|
+
* default buffered transport that delegates upload/download/delete to it.
|
|
73
|
+
* - `transportAdapter` — an {@link AttachmentTransportAdapter} that owns all remote
|
|
74
|
+
* operations directly (e.g. a native file-URI implementation for buffer-free
|
|
75
|
+
* transfer of large files). No `remoteStorage` is needed in this case.
|
|
76
|
+
*
|
|
77
|
+
* Supplying both, or neither, is a type error.
|
|
78
|
+
*
|
|
79
|
+
* @experimental
|
|
80
|
+
* @alpha This is currently experimental and may change without a major version bump.
|
|
81
|
+
*/
|
|
82
|
+
export type AttachmentQueueOptions<TLocal extends LocalStorageAdapter = LocalStorageAdapter> =
|
|
83
|
+
BaseAttachmentQueueOptions<TLocal> &
|
|
84
|
+
(
|
|
85
|
+
| { remoteStorage: RemoteStorageAdapter; transportAdapter?: never }
|
|
86
|
+
| { transportAdapter: AttachmentTransportAdapter; remoteStorage?: never }
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Fields shared by {@link AttachmentQueue.saveFile} and {@link AttachmentQueue.saveFileFromUri}.
|
|
91
|
+
*
|
|
92
|
+
* @alpha
|
|
93
|
+
*/
|
|
94
|
+
export interface SaveAttachmentOptions {
|
|
95
|
+
/** File extension (e.g., 'jpg', 'pdf') */
|
|
96
|
+
fileExtension: string;
|
|
97
|
+
/** MIME type of the file (e.g., 'image/jpeg') */
|
|
98
|
+
mediaType?: string;
|
|
99
|
+
/** Optional metadata to associate with the attachment */
|
|
100
|
+
metaData?: string;
|
|
101
|
+
/** Optional custom ID. If not provided, a UUID will be generated */
|
|
102
|
+
id?: string;
|
|
103
|
+
/**
|
|
104
|
+
* Optional callback to execute additional database operations within the same transaction as the
|
|
105
|
+
* attachment creation.
|
|
106
|
+
*/
|
|
107
|
+
updateHook?: (transaction: Transaction, attachment: AttachmentRecord) => Promise<void>;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** How the file bytes reach managed storage when creating an upload attachment. */
|
|
111
|
+
type AttachmentSource = { kind: 'data'; data: AttachmentData } | { kind: 'uri'; localUri: string };
|
|
112
|
+
|
|
68
113
|
/**
|
|
69
114
|
* AttachmentQueue manages the lifecycle and synchronization of attachments
|
|
70
115
|
* between local and remote storage.
|
|
@@ -74,7 +119,7 @@ export interface AttachmentQueueOptions {
|
|
|
74
119
|
* @experimental
|
|
75
120
|
* @alpha This is currently experimental and may change without a major version bump.
|
|
76
121
|
*/
|
|
77
|
-
export class AttachmentQueue {
|
|
122
|
+
export class AttachmentQueue<TLocal extends LocalStorageAdapter = LocalStorageAdapter> {
|
|
78
123
|
/** Timer for periodic synchronization operations */
|
|
79
124
|
private periodicSyncTimer?: ReturnType<typeof setInterval>;
|
|
80
125
|
|
|
@@ -82,10 +127,7 @@ export class AttachmentQueue {
|
|
|
82
127
|
private readonly syncingService: SyncingService;
|
|
83
128
|
|
|
84
129
|
/** Adapter for local file storage operations */
|
|
85
|
-
readonly localStorage:
|
|
86
|
-
|
|
87
|
-
/** Adapter for remote file storage operations */
|
|
88
|
-
readonly remoteStorage: RemoteStorageAdapter;
|
|
130
|
+
readonly localStorage: TLocal;
|
|
89
131
|
|
|
90
132
|
/**
|
|
91
133
|
* Callback function to watch for changes in attachment references in your data model.
|
|
@@ -103,7 +145,7 @@ export class AttachmentQueue {
|
|
|
103
145
|
readonly tableName: string;
|
|
104
146
|
|
|
105
147
|
/** Logger instance for diagnostic information */
|
|
106
|
-
readonly logger:
|
|
148
|
+
readonly logger: PowerSyncLogger;
|
|
107
149
|
|
|
108
150
|
/** Interval in milliseconds between periodic sync operations. Acts as a polling timer to retry
|
|
109
151
|
* failed uploads/downloads, especially after the app goes offline. Default: 30000 (30 seconds) */
|
|
@@ -115,7 +157,7 @@ export class AttachmentQueue {
|
|
|
115
157
|
* quick succession (e.g., bulk inserts). This is distinct from syncIntervalMs — it controls
|
|
116
158
|
* how quickly the queue reacts to changes, while syncIntervalMs controls how often it polls
|
|
117
159
|
* for retries. Default: 30 (from DEFAULT_WATCH_THROTTLE_MS) */
|
|
118
|
-
readonly syncThrottleDuration
|
|
160
|
+
readonly syncThrottleDuration?: number;
|
|
119
161
|
|
|
120
162
|
/** Whether to automatically download remote attachments. Default: true */
|
|
121
163
|
readonly downloadAttachments: boolean = true;
|
|
@@ -127,7 +169,7 @@ export class AttachmentQueue {
|
|
|
127
169
|
private readonly attachmentService: AttachmentService;
|
|
128
170
|
|
|
129
171
|
/** PowerSync database instance */
|
|
130
|
-
private readonly db:
|
|
172
|
+
private readonly db: CommonPowerSyncDatabase;
|
|
131
173
|
|
|
132
174
|
/** Cleanup function for status change listener */
|
|
133
175
|
private statusListenerDispose?: () => void;
|
|
@@ -143,7 +185,7 @@ export class AttachmentQueue {
|
|
|
143
185
|
* processing don't take this lock and proceed in parallel via the
|
|
144
186
|
* `AttachmentService` mutex, which is acquired only briefly per row.
|
|
145
187
|
*/
|
|
146
|
-
private syncLoopMutex
|
|
188
|
+
private syncLoopMutex: Mutex;
|
|
147
189
|
|
|
148
190
|
/**
|
|
149
191
|
* Aborted by `stopSync()` to interrupt an in-flight batch within one
|
|
@@ -160,17 +202,18 @@ export class AttachmentQueue {
|
|
|
160
202
|
db,
|
|
161
203
|
localStorage,
|
|
162
204
|
remoteStorage,
|
|
205
|
+
transportAdapter,
|
|
163
206
|
watchAttachments,
|
|
164
207
|
logger,
|
|
165
208
|
tableName = ATTACHMENT_TABLE,
|
|
166
209
|
syncIntervalMs = 30 * 1000,
|
|
167
|
-
syncThrottleDuration
|
|
210
|
+
syncThrottleDuration,
|
|
168
211
|
downloadAttachments = true,
|
|
169
212
|
archivedCacheLimit = 100,
|
|
170
213
|
errorHandler
|
|
171
|
-
}: AttachmentQueueOptions) {
|
|
214
|
+
}: AttachmentQueueOptions<TLocal>) {
|
|
172
215
|
this.db = db;
|
|
173
|
-
this.
|
|
216
|
+
this.syncLoopMutex = db.createMutex();
|
|
174
217
|
this.localStorage = localStorage;
|
|
175
218
|
this.watchAttachments = watchAttachments;
|
|
176
219
|
this.tableName = tableName;
|
|
@@ -180,10 +223,13 @@ export class AttachmentQueue {
|
|
|
180
223
|
this.downloadAttachments = downloadAttachments;
|
|
181
224
|
this.logger = logger ?? db.logger;
|
|
182
225
|
this.attachmentService = new AttachmentService(db, this.logger, tableName, archivedCacheLimit);
|
|
226
|
+
|
|
227
|
+
const transport = transportAdapter ?? new BufferedAttachmentTransport(localStorage, remoteStorage!);
|
|
228
|
+
|
|
183
229
|
this.syncingService = new SyncingService(
|
|
184
230
|
this.attachmentService,
|
|
185
231
|
localStorage,
|
|
186
|
-
|
|
232
|
+
transport,
|
|
187
233
|
this.logger,
|
|
188
234
|
errorHandler
|
|
189
235
|
);
|
|
@@ -239,7 +285,7 @@ export class AttachmentQueue {
|
|
|
239
285
|
if (status.connected) {
|
|
240
286
|
// Device came online, process attachments immediately
|
|
241
287
|
this.syncStorage().catch((error) => {
|
|
242
|
-
this.logger.error
|
|
288
|
+
this.logger.log({ level: LogLevels.error, message: 'Error syncing storage on connection', error });
|
|
243
289
|
});
|
|
244
290
|
}
|
|
245
291
|
}
|
|
@@ -348,7 +394,8 @@ export class AttachmentQueue {
|
|
|
348
394
|
*/
|
|
349
395
|
async syncStorage(): Promise<void> {
|
|
350
396
|
const signal = this.syncAbortController?.signal;
|
|
351
|
-
|
|
397
|
+
// We have a signal from startSync() to stopSync(), so treat the absence of one like an aborted sync.
|
|
398
|
+
if (signal == null || signal?.aborted) return;
|
|
352
399
|
|
|
353
400
|
try {
|
|
354
401
|
await this.syncLoopMutex.runExclusive(async () => {
|
|
@@ -357,13 +404,13 @@ export class AttachmentQueue {
|
|
|
357
404
|
|
|
358
405
|
await this.syncingService.processAttachments(activeAttachments, { signal });
|
|
359
406
|
|
|
360
|
-
if (signal
|
|
407
|
+
if (signal.aborted) return;
|
|
361
408
|
|
|
362
409
|
await this.attachmentService.withContext((ctx) => this.syncingService.deleteArchivedAttachments(ctx));
|
|
363
410
|
}, signal);
|
|
364
411
|
} catch (error) {
|
|
365
412
|
// A queued batch's acquire rejects when `stopSync` aborts — expected, not an error.
|
|
366
|
-
if (signal
|
|
413
|
+
if (signal.aborted) return;
|
|
367
414
|
throw error;
|
|
368
415
|
}
|
|
369
416
|
}
|
|
@@ -407,49 +454,30 @@ export class AttachmentQueue {
|
|
|
407
454
|
return this.attachmentService.withContext(callback);
|
|
408
455
|
}
|
|
409
456
|
/**
|
|
410
|
-
*
|
|
411
|
-
*
|
|
412
|
-
*
|
|
413
|
-
* @returns Promise resolving to the created attachment record
|
|
457
|
+
* Creates a `QUEUED_UPLOAD` attachment record, placing the file at the managed
|
|
458
|
+
* `localUri` from the given `source`, and persists the record
|
|
459
|
+
* alongside the caller's `updateHook` in a single transaction.
|
|
414
460
|
*/
|
|
415
|
-
async
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
metaData,
|
|
420
|
-
id,
|
|
421
|
-
updateHook
|
|
422
|
-
}: {
|
|
423
|
-
/**
|
|
424
|
-
* The file data as ArrayBuffer, Blob, or base64 string
|
|
425
|
-
*/
|
|
426
|
-
data: AttachmentData;
|
|
427
|
-
/**
|
|
428
|
-
* File extension (e.g., 'jpg', 'pdf')
|
|
429
|
-
*/
|
|
430
|
-
fileExtension: string;
|
|
431
|
-
/**
|
|
432
|
-
* MIME type of the file (e.g., 'image/jpeg')
|
|
433
|
-
*/
|
|
434
|
-
mediaType?: string;
|
|
435
|
-
/**
|
|
436
|
-
* Optional metadata to associate with the attachment
|
|
437
|
-
*/
|
|
438
|
-
metaData?: string;
|
|
439
|
-
/**
|
|
440
|
-
* Optional custom ID. If not provided, a UUID will be generated
|
|
441
|
-
*/
|
|
442
|
-
id?: string;
|
|
443
|
-
/**
|
|
444
|
-
* Optional callback to execute additional database operations within the same transaction as the attachment
|
|
445
|
-
* creation.
|
|
446
|
-
*/
|
|
447
|
-
updateHook?: (transaction: Transaction, attachment: AttachmentRecord) => Promise<void>;
|
|
448
|
-
}): Promise<AttachmentRecord> {
|
|
461
|
+
private async createUploadAttachment(
|
|
462
|
+
{ fileExtension, mediaType, metaData, id, updateHook }: SaveAttachmentOptions,
|
|
463
|
+
source: AttachmentSource
|
|
464
|
+
): Promise<AttachmentRecord> {
|
|
449
465
|
const resolvedId = id ?? (await this.generateAttachmentId());
|
|
450
466
|
const filename = `${resolvedId}.${fileExtension}`;
|
|
451
467
|
const localUri = this.localStorage.getLocalUri(filename);
|
|
452
|
-
|
|
468
|
+
|
|
469
|
+
let size: number;
|
|
470
|
+
if (source.kind === 'data') {
|
|
471
|
+
size = await this.localStorage.saveFile(localUri, source.data);
|
|
472
|
+
} else {
|
|
473
|
+
// saveFileFromUri is only exposed for streaming-capable local adapters; guard at
|
|
474
|
+
// runtime too for plain-JS callers.
|
|
475
|
+
const localStorage = this.localStorage as Partial<StreamingLocalStorageAdapter>;
|
|
476
|
+
if (!localStorage.moveFile) {
|
|
477
|
+
throw new Error('The configured local storage adapter does not support moveFile, required by saveFileFromUri.');
|
|
478
|
+
}
|
|
479
|
+
size = await localStorage.moveFile(source.localUri, localUri);
|
|
480
|
+
}
|
|
453
481
|
|
|
454
482
|
const attachment: AttachmentRecord = {
|
|
455
483
|
id: resolvedId,
|
|
@@ -473,6 +501,37 @@ export class AttachmentQueue {
|
|
|
473
501
|
return attachment;
|
|
474
502
|
}
|
|
475
503
|
|
|
504
|
+
/**
|
|
505
|
+
* Saves in-memory file data to local storage and queues it for upload.
|
|
506
|
+
*
|
|
507
|
+
* @param options - File data plus {@link SaveAttachmentOptions}
|
|
508
|
+
* @returns Promise resolving to the created attachment record
|
|
509
|
+
*/
|
|
510
|
+
async saveFile(options: SaveAttachmentOptions & { data: AttachmentData }): Promise<AttachmentRecord> {
|
|
511
|
+
return this.createUploadAttachment(options, { kind: 'data', data: options.data });
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Registers a file that already exists on disk and queues it for upload, moving it
|
|
516
|
+
* into managed storage without loading it into memory.
|
|
517
|
+
*
|
|
518
|
+
* Prefer this over {@link AttachmentQueue.saveFile} for large, app-originated files
|
|
519
|
+
* (recordings, videos): it avoids reading the file into an `ArrayBuffer` just to write
|
|
520
|
+
* it back to disk. Requires the local storage adapter to implement `moveFile`.
|
|
521
|
+
*
|
|
522
|
+
* Only available when the queue is configured with a {@link StreamingLocalStorageAdapter}
|
|
523
|
+
* (one that implements `moveFile`).
|
|
524
|
+
*
|
|
525
|
+
* @param options - The existing file's `localUri` plus {@link SaveAttachmentOptions}
|
|
526
|
+
* @returns Promise resolving to the created attachment record
|
|
527
|
+
*/
|
|
528
|
+
async saveFileFromUri(
|
|
529
|
+
this: AttachmentQueue<StreamingLocalStorageAdapter>,
|
|
530
|
+
options: SaveAttachmentOptions & { localUri: string }
|
|
531
|
+
): Promise<AttachmentRecord> {
|
|
532
|
+
return this.createUploadAttachment(options, { kind: 'uri', localUri: options.localUri });
|
|
533
|
+
}
|
|
534
|
+
|
|
476
535
|
async deleteFile({
|
|
477
536
|
id,
|
|
478
537
|
updateHook
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CommonPowerSyncDatabase } from '../client/CommonPowerSyncDatabase.js';
|
|
2
2
|
import { DifferentialWatchedQuery } from '../client/watched/processors/DifferentialQueryProcessor.js';
|
|
3
|
-
import {
|
|
3
|
+
import { PowerSyncLogger, LogLevels } from '../utils/Logger.js';
|
|
4
4
|
import { Mutex } from '../utils/mutex.js';
|
|
5
5
|
import { AttachmentContext } from './AttachmentContext.js';
|
|
6
6
|
import { AttachmentRecord, AttachmentState } from './Schema.js';
|
|
@@ -11,15 +11,16 @@ import { AttachmentRecord, AttachmentState } from './Schema.js';
|
|
|
11
11
|
* @internal
|
|
12
12
|
*/
|
|
13
13
|
export class AttachmentService {
|
|
14
|
-
private mutex
|
|
14
|
+
private mutex: Mutex;
|
|
15
15
|
private context: AttachmentContext;
|
|
16
16
|
|
|
17
17
|
constructor(
|
|
18
|
-
private db:
|
|
19
|
-
private logger:
|
|
18
|
+
private db: CommonPowerSyncDatabase,
|
|
19
|
+
private logger: PowerSyncLogger,
|
|
20
20
|
private tableName: string = 'attachments',
|
|
21
21
|
archivedCacheLimit: number = 100
|
|
22
22
|
) {
|
|
23
|
+
this.mutex = db.createMutex();
|
|
23
24
|
this.context = new AttachmentContext(db, tableName, logger, archivedCacheLimit);
|
|
24
25
|
}
|
|
25
26
|
|
|
@@ -28,7 +29,7 @@ export class AttachmentService {
|
|
|
28
29
|
* @returns Watch query that emits changes for queued uploads, downloads, and deletes
|
|
29
30
|
*/
|
|
30
31
|
watchActiveAttachments({ throttleMs }: { throttleMs?: number } = {}): DifferentialWatchedQuery<AttachmentRecord> {
|
|
31
|
-
this.logger.info
|
|
32
|
+
this.logger.log({ level: LogLevels.info, message: 'Watching active attachments...' });
|
|
32
33
|
const watch = this.db
|
|
33
34
|
.query<AttachmentRecord>({
|
|
34
35
|
sql: /* sql */ `
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { AttachmentRecord } from './Schema.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* An {@link AttachmentRecord} that is guaranteed to have a `localUri`.
|
|
5
|
+
*
|
|
6
|
+
* The syncing service assigns `localUri` before invoking a transport download,
|
|
7
|
+
* so implementations always receive both the metadata and the destination path.
|
|
8
|
+
*
|
|
9
|
+
* @alpha
|
|
10
|
+
*/
|
|
11
|
+
export type LocatedAttachmentRecord = AttachmentRecord & { localUri: string };
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* AttachmentTransportAdapter owns all remote-side operations for an attachment —
|
|
15
|
+
* transfer (upload/download) and delete — as single operations.
|
|
16
|
+
*
|
|
17
|
+
* A transport owns the entire transfer, letting implementations pick the most
|
|
18
|
+
* efficient mechanism available (buffer, stream, or a platform-native file-URI
|
|
19
|
+
* upload/download API). On platforms like React Native this allows large files to
|
|
20
|
+
* be transferred without ever materializing them in the JS heap.
|
|
21
|
+
*
|
|
22
|
+
* The default transport composes the local and remote storage adapters. Provide a custom transport (via
|
|
23
|
+
* `AttachmentQueue`'s `transportAdapter` option) to own the whole remote side; in
|
|
24
|
+
* that case a separate `remoteStorage` is not required.
|
|
25
|
+
*
|
|
26
|
+
* @experimental
|
|
27
|
+
* @alpha This is currently experimental and may change without a major version bump.
|
|
28
|
+
*/
|
|
29
|
+
export interface AttachmentTransportAdapter {
|
|
30
|
+
/**
|
|
31
|
+
* Uploads the attachment's local file to remote storage.
|
|
32
|
+
* @param attachment - The attachment to upload. `localUri` points at the source file.
|
|
33
|
+
*/
|
|
34
|
+
upload(attachment: LocatedAttachmentRecord): Promise<void>;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Downloads the remote file into `attachment.localUri`.
|
|
38
|
+
* @param attachment - The attachment to download. `localUri` is the destination path,
|
|
39
|
+
* assigned by the syncing service before this call.
|
|
40
|
+
*/
|
|
41
|
+
download(attachment: LocatedAttachmentRecord): Promise<void>;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Deletes the attachment's file from remote storage. Local file removal is handled
|
|
45
|
+
* separately by the syncing service.
|
|
46
|
+
* @param attachment - The attachment to delete.
|
|
47
|
+
*/
|
|
48
|
+
delete(attachment: AttachmentRecord): Promise<void>;
|
|
49
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { AttachmentTransportAdapter, LocatedAttachmentRecord } from './AttachmentTransportAdapter.js';
|
|
2
|
+
import { LocalStorageAdapter } from './LocalStorageAdapter.js';
|
|
3
|
+
import { RemoteStorageAdapter } from './RemoteStorageAdapter.js';
|
|
4
|
+
import { AttachmentRecord } from './Schema.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Default {@link AttachmentTransportAdapter}, composing the local and remote
|
|
8
|
+
* storage adapters.
|
|
9
|
+
*
|
|
10
|
+
* The full file body is materialized as an `ArrayBuffer` in JS memory between the
|
|
11
|
+
* two adapter calls. This is fine for small files but can cause memory pressure on
|
|
12
|
+
* large ones — environments that support native transfer should provide a custom
|
|
13
|
+
* transport instead.
|
|
14
|
+
*
|
|
15
|
+
* @experimental
|
|
16
|
+
* @alpha This is currently experimental and may change without a major version bump.
|
|
17
|
+
*/
|
|
18
|
+
export class BufferedAttachmentTransport implements AttachmentTransportAdapter {
|
|
19
|
+
constructor(
|
|
20
|
+
private localStorage: LocalStorageAdapter,
|
|
21
|
+
private remoteStorage: RemoteStorageAdapter
|
|
22
|
+
) {}
|
|
23
|
+
|
|
24
|
+
async upload(attachment: LocatedAttachmentRecord): Promise<void> {
|
|
25
|
+
const fileData = await this.localStorage.readFile(attachment.localUri);
|
|
26
|
+
await this.remoteStorage.uploadFile(fileData, attachment);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async download(attachment: LocatedAttachmentRecord): Promise<void> {
|
|
30
|
+
const fileData = await this.remoteStorage.downloadFile(attachment);
|
|
31
|
+
await this.localStorage.saveFile(attachment.localUri, fileData);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async delete(attachment: AttachmentRecord): Promise<void> {
|
|
35
|
+
await this.remoteStorage.deleteFile(attachment);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -76,3 +76,23 @@ export interface LocalStorageAdapter {
|
|
|
76
76
|
*/
|
|
77
77
|
getLocalUri(filename: string): string;
|
|
78
78
|
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* A {@link LocalStorageAdapter} that can relocate a file into managed storage without
|
|
82
|
+
* loading it into memory. Required for {@link AttachmentQueue.saveFileFromUri}; only
|
|
83
|
+
* queues configured with a streaming-capable local adapter expose that method.
|
|
84
|
+
*
|
|
85
|
+
* @experimental
|
|
86
|
+
* @alpha This is currently experimental and may change without a major version bump.
|
|
87
|
+
*/
|
|
88
|
+
export interface StreamingLocalStorageAdapter extends LocalStorageAdapter {
|
|
89
|
+
/**
|
|
90
|
+
* Moves a file into managed storage without loading it into memory.
|
|
91
|
+
* Overwrites any existing file at the target. When source and target are the same
|
|
92
|
+
* path, this is a no-op that just reports the size.
|
|
93
|
+
* @param sourceUri - Path of the existing file
|
|
94
|
+
* @param targetUri - Destination path within managed storage
|
|
95
|
+
* @returns Number of bytes in the moved file
|
|
96
|
+
*/
|
|
97
|
+
moveFile(sourceUri: string, targetUri: string): Promise<number>;
|
|
98
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { column } from '../db/schema/Column.js';
|
|
2
2
|
import { RowType, Table } from '../db/schema/Table.js';
|
|
3
|
-
import {
|
|
3
|
+
import { TableOptions } from '../db/schema/Table.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* The default name of the local table storing attachment data.
|
|
@@ -64,7 +64,7 @@ export enum AttachmentState {
|
|
|
64
64
|
/**
|
|
65
65
|
* @alpha
|
|
66
66
|
*/
|
|
67
|
-
export interface AttachmentTableOptions extends Omit<
|
|
67
|
+
export interface AttachmentTableOptions extends Omit<TableOptions, 'name' | 'columns'> {}
|
|
68
68
|
|
|
69
69
|
/**
|
|
70
70
|
* @alpha
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { LogLevels, PowerSyncLogger } from '../utils/Logger.js';
|
|
2
2
|
import { AttachmentService } from './AttachmentService.js';
|
|
3
|
+
import { AttachmentTransportAdapter } from './AttachmentTransportAdapter.js';
|
|
3
4
|
import { LocalStorageAdapter } from './LocalStorageAdapter.js';
|
|
4
|
-
import { RemoteStorageAdapter } from './RemoteStorageAdapter.js';
|
|
5
5
|
import { AttachmentRecord, AttachmentState } from './Schema.js';
|
|
6
6
|
import { AttachmentErrorHandler } from './AttachmentErrorHandler.js';
|
|
7
7
|
import { AttachmentContext } from './AttachmentContext.js';
|
|
@@ -10,25 +10,28 @@ import { AttachmentContext } from './AttachmentContext.js';
|
|
|
10
10
|
* Orchestrates attachment synchronization between local and remote storage.
|
|
11
11
|
* Handles uploads, downloads, deletions, and state transitions.
|
|
12
12
|
*
|
|
13
|
+
* Remote operations (upload/download/delete) go through the {@link AttachmentTransportAdapter};
|
|
14
|
+
* local file operations use the {@link LocalStorageAdapter}.
|
|
15
|
+
*
|
|
13
16
|
* @internal
|
|
14
17
|
*/
|
|
15
18
|
export class SyncingService {
|
|
16
19
|
private attachmentService: AttachmentService;
|
|
17
20
|
private localStorage: LocalStorageAdapter;
|
|
18
|
-
private
|
|
19
|
-
private logger:
|
|
21
|
+
private transport: AttachmentTransportAdapter;
|
|
22
|
+
private logger: PowerSyncLogger;
|
|
20
23
|
private errorHandler?: AttachmentErrorHandler;
|
|
21
24
|
|
|
22
25
|
constructor(
|
|
23
26
|
attachmentService: AttachmentService,
|
|
24
27
|
localStorage: LocalStorageAdapter,
|
|
25
|
-
|
|
26
|
-
logger:
|
|
28
|
+
transport: AttachmentTransportAdapter,
|
|
29
|
+
logger: PowerSyncLogger,
|
|
27
30
|
errorHandler?: AttachmentErrorHandler
|
|
28
31
|
) {
|
|
29
32
|
this.attachmentService = attachmentService;
|
|
30
33
|
this.localStorage = localStorage;
|
|
31
|
-
this.
|
|
34
|
+
this.transport = transport;
|
|
32
35
|
this.logger = logger;
|
|
33
36
|
this.errorHandler = errorHandler;
|
|
34
37
|
}
|
|
@@ -56,11 +59,17 @@ export class SyncingService {
|
|
|
56
59
|
}
|
|
57
60
|
): Promise<void> {
|
|
58
61
|
const signal = options?.signal;
|
|
59
|
-
this.logger.
|
|
62
|
+
this.logger.log({
|
|
63
|
+
level: LogLevels.info,
|
|
64
|
+
message: `Starting processAttachments with ${attachments.length} attachments`
|
|
65
|
+
});
|
|
60
66
|
|
|
61
67
|
for (const attachment of attachments) {
|
|
62
68
|
if (signal?.aborted) {
|
|
63
|
-
this.logger.
|
|
69
|
+
this.logger.log({
|
|
70
|
+
level: LogLevels.info,
|
|
71
|
+
message: 'Sync cancelled; stopping iteration early'
|
|
72
|
+
});
|
|
64
73
|
return;
|
|
65
74
|
}
|
|
66
75
|
|
|
@@ -84,7 +93,11 @@ export class SyncingService {
|
|
|
84
93
|
|
|
85
94
|
await this.attachmentService.withContext((ctx) => ctx.saveAttachments([updated]));
|
|
86
95
|
} catch (error) {
|
|
87
|
-
this.logger.
|
|
96
|
+
this.logger.log({
|
|
97
|
+
level: LogLevels.warn,
|
|
98
|
+
message: `Error during sync for ${attachment.id}`,
|
|
99
|
+
error
|
|
100
|
+
});
|
|
88
101
|
}
|
|
89
102
|
}
|
|
90
103
|
}
|
|
@@ -98,14 +111,13 @@ export class SyncingService {
|
|
|
98
111
|
* @throws Error if the attachment has no localUri
|
|
99
112
|
*/
|
|
100
113
|
async uploadAttachment(attachment: AttachmentRecord): Promise<AttachmentRecord> {
|
|
101
|
-
this.logger.info
|
|
114
|
+
this.logger.log({ level: LogLevels.info, message: `Uploading attachment ${attachment.filename}` });
|
|
102
115
|
try {
|
|
103
116
|
if (attachment.localUri == null) {
|
|
104
117
|
throw new Error(`No localUri for attachment ${attachment.id}`);
|
|
105
118
|
}
|
|
106
119
|
|
|
107
|
-
|
|
108
|
-
await this.remoteStorage.uploadFile(fileBlob, attachment);
|
|
120
|
+
await this.transport.upload({ ...attachment, localUri: attachment.localUri });
|
|
109
121
|
|
|
110
122
|
return {
|
|
111
123
|
...attachment,
|
|
@@ -127,19 +139,17 @@ export class SyncingService {
|
|
|
127
139
|
|
|
128
140
|
/**
|
|
129
141
|
* Downloads an attachment from remote storage to local storage.
|
|
130
|
-
*
|
|
142
|
+
* The destination `localUri` is assigned here and the transport writes the file to it.
|
|
131
143
|
* On success, marks as SYNCED. On failure, defers to error handler or archives.
|
|
132
144
|
*
|
|
133
145
|
* @param attachment - The attachment record to download
|
|
134
146
|
* @returns Updated attachment record with local URI and new state
|
|
135
147
|
*/
|
|
136
148
|
async downloadAttachment(attachment: AttachmentRecord): Promise<AttachmentRecord> {
|
|
137
|
-
this.logger.info
|
|
149
|
+
this.logger.log({ level: LogLevels.info, message: `Downloading attachment ${attachment.filename}` });
|
|
138
150
|
try {
|
|
139
|
-
const fileData = await this.remoteStorage.downloadFile(attachment);
|
|
140
|
-
|
|
141
151
|
const localUri = this.localStorage.getLocalUri(attachment.filename);
|
|
142
|
-
await this.
|
|
152
|
+
await this.transport.download({ ...attachment, localUri });
|
|
143
153
|
|
|
144
154
|
return {
|
|
145
155
|
...attachment,
|
|
@@ -171,7 +181,7 @@ export class SyncingService {
|
|
|
171
181
|
*/
|
|
172
182
|
async deleteAttachment(attachment: AttachmentRecord, context: AttachmentContext): Promise<AttachmentRecord> {
|
|
173
183
|
try {
|
|
174
|
-
await this.
|
|
184
|
+
await this.transport.delete(attachment);
|
|
175
185
|
if (attachment.localUri) {
|
|
176
186
|
await this.localStorage.deleteFile(attachment.localUri);
|
|
177
187
|
}
|
|
@@ -206,7 +216,11 @@ export class SyncingService {
|
|
|
206
216
|
try {
|
|
207
217
|
await this.localStorage.deleteFile(attachment.localUri);
|
|
208
218
|
} catch (error) {
|
|
209
|
-
this.logger.
|
|
219
|
+
this.logger.log({
|
|
220
|
+
level: LogLevels.error,
|
|
221
|
+
message: 'Error deleting local file for archived attachment',
|
|
222
|
+
error
|
|
223
|
+
});
|
|
210
224
|
}
|
|
211
225
|
}
|
|
212
226
|
}
|