@powersync/common 1.46.0 → 1.47.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/README.md +5 -1
- package/dist/bundle.cjs +1266 -360
- package/dist/bundle.cjs.map +1 -1
- package/dist/bundle.mjs +1259 -361
- package/dist/bundle.mjs.map +1 -1
- package/dist/bundle.node.cjs +1266 -360
- package/dist/bundle.node.cjs.map +1 -1
- package/dist/bundle.node.mjs +1259 -361
- package/dist/bundle.node.mjs.map +1 -1
- package/dist/index.d.cts +530 -29
- package/lib/attachments/AttachmentContext.d.ts +86 -0
- package/lib/attachments/AttachmentContext.js +229 -0
- package/lib/attachments/AttachmentContext.js.map +1 -0
- package/lib/attachments/AttachmentErrorHandler.d.ts +31 -0
- package/lib/attachments/AttachmentErrorHandler.js +2 -0
- package/lib/attachments/AttachmentErrorHandler.js.map +1 -0
- package/lib/attachments/AttachmentQueue.d.ts +149 -0
- package/lib/attachments/AttachmentQueue.js +362 -0
- package/lib/attachments/AttachmentQueue.js.map +1 -0
- package/lib/attachments/AttachmentService.d.ts +29 -0
- package/lib/attachments/AttachmentService.js +56 -0
- package/lib/attachments/AttachmentService.js.map +1 -0
- package/lib/attachments/LocalStorageAdapter.d.ts +62 -0
- package/lib/attachments/LocalStorageAdapter.js +6 -0
- package/lib/attachments/LocalStorageAdapter.js.map +1 -0
- package/lib/attachments/RemoteStorageAdapter.d.ts +27 -0
- package/lib/attachments/RemoteStorageAdapter.js +2 -0
- package/lib/attachments/RemoteStorageAdapter.js.map +1 -0
- package/lib/attachments/Schema.d.ts +50 -0
- package/lib/attachments/Schema.js +62 -0
- package/lib/attachments/Schema.js.map +1 -0
- package/lib/attachments/SyncingService.d.ts +62 -0
- package/lib/attachments/SyncingService.js +168 -0
- package/lib/attachments/SyncingService.js.map +1 -0
- package/lib/attachments/WatchedAttachmentItem.d.ts +17 -0
- package/lib/attachments/WatchedAttachmentItem.js +2 -0
- package/lib/attachments/WatchedAttachmentItem.js.map +1 -0
- package/lib/index.d.ts +10 -0
- package/lib/index.js +10 -0
- package/lib/index.js.map +1 -1
- package/lib/utils/mutex.d.ts +1 -1
- package/lib/utils/mutex.js.map +1 -1
- package/package.json +1 -1
- package/src/attachments/AttachmentContext.ts +279 -0
- package/src/attachments/AttachmentErrorHandler.ts +34 -0
- package/src/attachments/AttachmentQueue.ts +472 -0
- package/src/attachments/AttachmentService.ts +62 -0
- package/src/attachments/LocalStorageAdapter.ts +72 -0
- package/src/attachments/README.md +718 -0
- package/src/attachments/RemoteStorageAdapter.ts +30 -0
- package/src/attachments/Schema.ts +87 -0
- package/src/attachments/SyncingService.ts +193 -0
- package/src/attachments/WatchedAttachmentItem.ts +19 -0
- package/src/index.ts +11 -0
- package/src/utils/mutex.ts +1 -1
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { AttachmentState, attachmentFromSql } from './Schema.js';
|
|
2
|
+
/**
|
|
3
|
+
* AttachmentContext provides database operations for managing attachment records.
|
|
4
|
+
*
|
|
5
|
+
* Provides methods to query, insert, update, and delete attachment records with
|
|
6
|
+
* proper transaction management through PowerSync.
|
|
7
|
+
*
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
export class AttachmentContext {
|
|
11
|
+
/** PowerSync database instance for executing queries */
|
|
12
|
+
db;
|
|
13
|
+
/** Name of the database table storing attachment records */
|
|
14
|
+
tableName;
|
|
15
|
+
/** Logger instance for diagnostic information */
|
|
16
|
+
logger;
|
|
17
|
+
/** Maximum number of archived attachments to keep before cleanup */
|
|
18
|
+
archivedCacheLimit = 100;
|
|
19
|
+
/**
|
|
20
|
+
* Creates a new AttachmentContext instance.
|
|
21
|
+
*
|
|
22
|
+
* @param db - PowerSync database instance
|
|
23
|
+
* @param tableName - Name of the table storing attachment records. Default: 'attachments'
|
|
24
|
+
* @param logger - Logger instance for diagnostic output
|
|
25
|
+
*/
|
|
26
|
+
constructor(db, tableName = 'attachments', logger, archivedCacheLimit) {
|
|
27
|
+
this.db = db;
|
|
28
|
+
this.tableName = tableName;
|
|
29
|
+
this.logger = logger;
|
|
30
|
+
this.archivedCacheLimit = archivedCacheLimit;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Retrieves all active attachments that require synchronization.
|
|
34
|
+
* Active attachments include those queued for upload, download, or delete.
|
|
35
|
+
* Results are ordered by timestamp in ascending order.
|
|
36
|
+
*
|
|
37
|
+
* @returns Promise resolving to an array of active attachment records
|
|
38
|
+
*/
|
|
39
|
+
async getActiveAttachments() {
|
|
40
|
+
const attachments = await this.db.getAll(
|
|
41
|
+
/* sql */
|
|
42
|
+
`
|
|
43
|
+
SELECT
|
|
44
|
+
*
|
|
45
|
+
FROM
|
|
46
|
+
${this.tableName}
|
|
47
|
+
WHERE
|
|
48
|
+
state = ?
|
|
49
|
+
OR state = ?
|
|
50
|
+
OR state = ?
|
|
51
|
+
ORDER BY
|
|
52
|
+
timestamp ASC
|
|
53
|
+
`, [AttachmentState.QUEUED_UPLOAD, AttachmentState.QUEUED_DOWNLOAD, AttachmentState.QUEUED_DELETE]);
|
|
54
|
+
return attachments.map(attachmentFromSql);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Retrieves all archived attachments.
|
|
58
|
+
*
|
|
59
|
+
* Archived attachments are no longer referenced but haven't been permanently deleted.
|
|
60
|
+
* These are candidates for cleanup operations to free up storage space.
|
|
61
|
+
*
|
|
62
|
+
* @returns Promise resolving to an array of archived attachment records
|
|
63
|
+
*/
|
|
64
|
+
async getArchivedAttachments() {
|
|
65
|
+
const attachments = await this.db.getAll(
|
|
66
|
+
/* sql */
|
|
67
|
+
`
|
|
68
|
+
SELECT
|
|
69
|
+
*
|
|
70
|
+
FROM
|
|
71
|
+
${this.tableName}
|
|
72
|
+
WHERE
|
|
73
|
+
state = ?
|
|
74
|
+
ORDER BY
|
|
75
|
+
timestamp ASC
|
|
76
|
+
`, [AttachmentState.ARCHIVED]);
|
|
77
|
+
return attachments.map(attachmentFromSql);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Retrieves all attachment records regardless of state.
|
|
81
|
+
* Results are ordered by timestamp in ascending order.
|
|
82
|
+
*
|
|
83
|
+
* @returns Promise resolving to an array of all attachment records
|
|
84
|
+
*/
|
|
85
|
+
async getAttachments() {
|
|
86
|
+
const attachments = await this.db.getAll(
|
|
87
|
+
/* sql */
|
|
88
|
+
`
|
|
89
|
+
SELECT
|
|
90
|
+
*
|
|
91
|
+
FROM
|
|
92
|
+
${this.tableName}
|
|
93
|
+
ORDER BY
|
|
94
|
+
timestamp ASC
|
|
95
|
+
`, []);
|
|
96
|
+
return attachments.map(attachmentFromSql);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Inserts or updates an attachment record within an existing transaction.
|
|
100
|
+
*
|
|
101
|
+
* Performs an upsert operation (INSERT OR REPLACE). Must be called within
|
|
102
|
+
* an active database transaction context.
|
|
103
|
+
*
|
|
104
|
+
* @param attachment - The attachment record to upsert
|
|
105
|
+
* @param context - Active database transaction context
|
|
106
|
+
*/
|
|
107
|
+
async upsertAttachment(attachment, context) {
|
|
108
|
+
await context.execute(
|
|
109
|
+
/* sql */
|
|
110
|
+
`
|
|
111
|
+
INSERT
|
|
112
|
+
OR REPLACE INTO ${this.tableName} (
|
|
113
|
+
id,
|
|
114
|
+
filename,
|
|
115
|
+
local_uri,
|
|
116
|
+
size,
|
|
117
|
+
media_type,
|
|
118
|
+
timestamp,
|
|
119
|
+
state,
|
|
120
|
+
has_synced,
|
|
121
|
+
meta_data
|
|
122
|
+
)
|
|
123
|
+
VALUES
|
|
124
|
+
(?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
125
|
+
`, [
|
|
126
|
+
attachment.id,
|
|
127
|
+
attachment.filename,
|
|
128
|
+
attachment.localUri || null,
|
|
129
|
+
attachment.size || null,
|
|
130
|
+
attachment.mediaType || null,
|
|
131
|
+
attachment.timestamp,
|
|
132
|
+
attachment.state,
|
|
133
|
+
attachment.hasSynced ? 1 : 0,
|
|
134
|
+
attachment.metaData || null
|
|
135
|
+
]);
|
|
136
|
+
}
|
|
137
|
+
async getAttachment(id) {
|
|
138
|
+
const attachment = await this.db.get(
|
|
139
|
+
/* sql */
|
|
140
|
+
`
|
|
141
|
+
SELECT
|
|
142
|
+
*
|
|
143
|
+
FROM
|
|
144
|
+
${this.tableName}
|
|
145
|
+
WHERE
|
|
146
|
+
id = ?
|
|
147
|
+
`, [id]);
|
|
148
|
+
return attachment ? attachmentFromSql(attachment) : undefined;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Permanently deletes an attachment record from the database.
|
|
152
|
+
*
|
|
153
|
+
* This operation removes the attachment record but does not delete
|
|
154
|
+
* the associated local or remote files. File deletion should be handled
|
|
155
|
+
* separately through the appropriate storage adapters.
|
|
156
|
+
*
|
|
157
|
+
* @param attachmentId - Unique identifier of the attachment to delete
|
|
158
|
+
*/
|
|
159
|
+
async deleteAttachment(attachmentId) {
|
|
160
|
+
await this.db.writeTransaction((tx) => tx.execute(
|
|
161
|
+
/* sql */
|
|
162
|
+
`
|
|
163
|
+
DELETE FROM ${this.tableName}
|
|
164
|
+
WHERE
|
|
165
|
+
id = ?
|
|
166
|
+
`, [attachmentId]));
|
|
167
|
+
}
|
|
168
|
+
async clearQueue() {
|
|
169
|
+
await this.db.writeTransaction((tx) => tx.execute(/* sql */ ` DELETE FROM ${this.tableName} `));
|
|
170
|
+
}
|
|
171
|
+
async deleteArchivedAttachments(callback) {
|
|
172
|
+
const limit = 1000;
|
|
173
|
+
const results = await this.db.getAll(
|
|
174
|
+
/* sql */
|
|
175
|
+
`
|
|
176
|
+
SELECT
|
|
177
|
+
*
|
|
178
|
+
FROM
|
|
179
|
+
${this.tableName}
|
|
180
|
+
WHERE
|
|
181
|
+
state = ?
|
|
182
|
+
ORDER BY
|
|
183
|
+
timestamp DESC
|
|
184
|
+
LIMIT
|
|
185
|
+
?
|
|
186
|
+
OFFSET
|
|
187
|
+
?
|
|
188
|
+
`, [AttachmentState.ARCHIVED, limit, this.archivedCacheLimit]);
|
|
189
|
+
const archivedAttachments = results.map(attachmentFromSql);
|
|
190
|
+
if (archivedAttachments.length === 0)
|
|
191
|
+
return false;
|
|
192
|
+
await callback?.(archivedAttachments);
|
|
193
|
+
this.logger.info(`Deleting ${archivedAttachments.length} archived attachments. Archived attachment exceeds cache archiveCacheLimit of ${this.archivedCacheLimit}.`);
|
|
194
|
+
const ids = archivedAttachments.map((attachment) => attachment.id);
|
|
195
|
+
await this.db.execute(
|
|
196
|
+
/* sql */
|
|
197
|
+
`
|
|
198
|
+
DELETE FROM ${this.tableName}
|
|
199
|
+
WHERE
|
|
200
|
+
id IN (
|
|
201
|
+
SELECT
|
|
202
|
+
json_each.value
|
|
203
|
+
FROM
|
|
204
|
+
json_each (?)
|
|
205
|
+
);
|
|
206
|
+
`, [JSON.stringify(ids)]);
|
|
207
|
+
this.logger.info(`Deleted ${archivedAttachments.length} archived attachments`);
|
|
208
|
+
return archivedAttachments.length < limit;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Saves multiple attachment records in a single transaction.
|
|
212
|
+
*
|
|
213
|
+
* All updates are saved in a single batch after processing.
|
|
214
|
+
* If the attachments array is empty, no database operations are performed.
|
|
215
|
+
*
|
|
216
|
+
* @param attachments - Array of attachment records to save
|
|
217
|
+
*/
|
|
218
|
+
async saveAttachments(attachments) {
|
|
219
|
+
if (attachments.length === 0) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
await this.db.writeTransaction(async (tx) => {
|
|
223
|
+
for (const attachment of attachments) {
|
|
224
|
+
await this.upsertAttachment(attachment, tx);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
//# sourceMappingURL=AttachmentContext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AttachmentContext.js","sourceRoot":"","sources":["../../src/attachments/AttachmentContext.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { AttachmentRecord } from './Schema.js';
|
|
2
|
+
/**
|
|
3
|
+
* SyncErrorHandler provides custom error handling for attachment sync operations.
|
|
4
|
+
* Implementations determine whether failed operations should be retried or archived.
|
|
5
|
+
*
|
|
6
|
+
* @experimental
|
|
7
|
+
* @alpha This is currently experimental and may change without a major version bump.
|
|
8
|
+
*/
|
|
9
|
+
export interface AttachmentErrorHandler {
|
|
10
|
+
/**
|
|
11
|
+
* Handles a download error for a specific attachment.
|
|
12
|
+
* @param attachment The attachment that failed to download
|
|
13
|
+
* @param error The error encountered during the download
|
|
14
|
+
* @returns `true` to retry the operation, `false` to archive the attachment
|
|
15
|
+
*/
|
|
16
|
+
onDownloadError(attachment: AttachmentRecord, error: unknown): Promise<boolean>;
|
|
17
|
+
/**
|
|
18
|
+
* Handles an upload error for a specific attachment.
|
|
19
|
+
* @param attachment The attachment that failed to upload
|
|
20
|
+
* @param error The error encountered during the upload
|
|
21
|
+
* @returns `true` to retry the operation, `false` to archive the attachment
|
|
22
|
+
*/
|
|
23
|
+
onUploadError(attachment: AttachmentRecord, error: unknown): Promise<boolean>;
|
|
24
|
+
/**
|
|
25
|
+
* Handles a delete error for a specific attachment.
|
|
26
|
+
* @param attachment The attachment that failed to delete
|
|
27
|
+
* @param error The error encountered during the delete
|
|
28
|
+
* @returns `true` to retry the operation, `false` to archive the attachment
|
|
29
|
+
*/
|
|
30
|
+
onDeleteError(attachment: AttachmentRecord, error: unknown): Promise<boolean>;
|
|
31
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AttachmentErrorHandler.js","sourceRoot":"","sources":["../../src/attachments/AttachmentErrorHandler.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { AbstractPowerSyncDatabase } from '../client/AbstractPowerSyncDatabase.js';
|
|
2
|
+
import { ILogger } from '../utils/Logger.js';
|
|
3
|
+
import { Transaction } from '../db/DBAdapter.js';
|
|
4
|
+
import { AttachmentData, LocalStorageAdapter } from './LocalStorageAdapter.js';
|
|
5
|
+
import { RemoteStorageAdapter } from './RemoteStorageAdapter.js';
|
|
6
|
+
import { AttachmentRecord } from './Schema.js';
|
|
7
|
+
import { WatchedAttachmentItem } from './WatchedAttachmentItem.js';
|
|
8
|
+
import { AttachmentErrorHandler } from './AttachmentErrorHandler.js';
|
|
9
|
+
/**
|
|
10
|
+
* AttachmentQueue manages the lifecycle and synchronization of attachments
|
|
11
|
+
* between local and remote storage.
|
|
12
|
+
* Provides automatic synchronization, upload/download queuing, attachment monitoring,
|
|
13
|
+
* verification and repair of local files, and cleanup of archived attachments.
|
|
14
|
+
*
|
|
15
|
+
* @experimental
|
|
16
|
+
* @alpha This is currently experimental and may change without a major version bump.
|
|
17
|
+
*/
|
|
18
|
+
export declare class AttachmentQueue {
|
|
19
|
+
/** Timer for periodic synchronization operations */
|
|
20
|
+
private periodicSyncTimer?;
|
|
21
|
+
/** Service for synchronizing attachments between local and remote storage */
|
|
22
|
+
private readonly syncingService;
|
|
23
|
+
/** Adapter for local file storage operations */
|
|
24
|
+
readonly localStorage: LocalStorageAdapter;
|
|
25
|
+
/** Adapter for remote file storage operations */
|
|
26
|
+
readonly remoteStorage: RemoteStorageAdapter;
|
|
27
|
+
/**
|
|
28
|
+
* Callback function to watch for changes in attachment references in your data model.
|
|
29
|
+
*
|
|
30
|
+
* This should be implemented by the user of AttachmentQueue to monitor changes in your application's
|
|
31
|
+
* data that reference attachments. When attachments are added, removed, or modified,
|
|
32
|
+
* this callback should trigger the onUpdate function with the current set of attachments.
|
|
33
|
+
*/
|
|
34
|
+
private readonly watchAttachments;
|
|
35
|
+
/** Name of the database table storing attachment records */
|
|
36
|
+
readonly tableName: string;
|
|
37
|
+
/** Logger instance for diagnostic information */
|
|
38
|
+
readonly logger: ILogger;
|
|
39
|
+
/** Interval in milliseconds between periodic sync operations. Default: 30000 (30 seconds) */
|
|
40
|
+
readonly syncIntervalMs: number;
|
|
41
|
+
/** Duration in milliseconds to throttle sync operations */
|
|
42
|
+
readonly syncThrottleDuration: number;
|
|
43
|
+
/** Whether to automatically download remote attachments. Default: true */
|
|
44
|
+
readonly downloadAttachments: boolean;
|
|
45
|
+
/** Maximum number of archived attachments to keep before cleanup. Default: 100 */
|
|
46
|
+
readonly archivedCacheLimit: number;
|
|
47
|
+
/** Service for managing attachment-related database operations */
|
|
48
|
+
private readonly attachmentService;
|
|
49
|
+
/** PowerSync database instance */
|
|
50
|
+
private readonly db;
|
|
51
|
+
/** Cleanup function for status change listener */
|
|
52
|
+
private statusListenerDispose?;
|
|
53
|
+
private watchActiveAttachments;
|
|
54
|
+
private watchAttachmentsAbortController;
|
|
55
|
+
/**
|
|
56
|
+
* Creates a new AttachmentQueue instance.
|
|
57
|
+
*
|
|
58
|
+
* @param options - Configuration options
|
|
59
|
+
* @param options.db - PowerSync database instance
|
|
60
|
+
* @param options.remoteStorage - Remote storage adapter for upload/download operations
|
|
61
|
+
* @param options.localStorage - Local storage adapter for file persistence
|
|
62
|
+
* @param options.watchAttachments - Callback for monitoring attachment changes in your data model
|
|
63
|
+
* @param options.tableName - Name of the table to store attachment records. Default: 'ps_attachment_queue'
|
|
64
|
+
* @param options.logger - Logger instance. Defaults to db.logger
|
|
65
|
+
* @param options.syncIntervalMs - Interval between automatic syncs in milliseconds. Default: 30000
|
|
66
|
+
* @param options.syncThrottleDuration - Throttle duration for sync operations in milliseconds. Default: 1000
|
|
67
|
+
* @param options.downloadAttachments - Whether to automatically download remote attachments. Default: true
|
|
68
|
+
* @param options.archivedCacheLimit - Maximum archived attachments before cleanup. Default: 100
|
|
69
|
+
*/
|
|
70
|
+
constructor({ db, localStorage, remoteStorage, watchAttachments, logger, tableName, syncIntervalMs, syncThrottleDuration, downloadAttachments, archivedCacheLimit, errorHandler }: {
|
|
71
|
+
db: AbstractPowerSyncDatabase;
|
|
72
|
+
remoteStorage: RemoteStorageAdapter;
|
|
73
|
+
localStorage: LocalStorageAdapter;
|
|
74
|
+
watchAttachments: (onUpdate: (attachment: WatchedAttachmentItem[]) => Promise<void>, signal: AbortSignal) => void;
|
|
75
|
+
tableName?: string;
|
|
76
|
+
logger?: ILogger;
|
|
77
|
+
syncIntervalMs?: number;
|
|
78
|
+
syncThrottleDuration?: number;
|
|
79
|
+
downloadAttachments?: boolean;
|
|
80
|
+
archivedCacheLimit?: number;
|
|
81
|
+
errorHandler?: AttachmentErrorHandler;
|
|
82
|
+
});
|
|
83
|
+
/**
|
|
84
|
+
* Generates a new attachment ID using a SQLite UUID function.
|
|
85
|
+
*
|
|
86
|
+
* @returns Promise resolving to the new attachment ID
|
|
87
|
+
*/
|
|
88
|
+
generateAttachmentId(): Promise<string>;
|
|
89
|
+
/**
|
|
90
|
+
* Starts the attachment synchronization process.
|
|
91
|
+
*
|
|
92
|
+
* This method:
|
|
93
|
+
* - Stops any existing sync operations
|
|
94
|
+
* - Sets up periodic synchronization based on syncIntervalMs
|
|
95
|
+
* - Registers listeners for active attachment changes
|
|
96
|
+
* - Processes watched attachments to queue uploads/downloads
|
|
97
|
+
* - Handles state transitions for archived and new attachments
|
|
98
|
+
*/
|
|
99
|
+
startSync(): Promise<void>;
|
|
100
|
+
/**
|
|
101
|
+
* Synchronizes all active attachments between local and remote storage.
|
|
102
|
+
*
|
|
103
|
+
* This is called automatically at regular intervals when sync is started,
|
|
104
|
+
* but can also be called manually to trigger an immediate sync.
|
|
105
|
+
*/
|
|
106
|
+
syncStorage(): Promise<void>;
|
|
107
|
+
/**
|
|
108
|
+
* Stops the attachment synchronization process.
|
|
109
|
+
*
|
|
110
|
+
* Clears the periodic sync timer and closes all active attachment watchers.
|
|
111
|
+
*/
|
|
112
|
+
stopSync(): Promise<void>;
|
|
113
|
+
/**
|
|
114
|
+
* Saves a file to local storage and queues it for upload to remote storage.
|
|
115
|
+
*
|
|
116
|
+
* @param options - File save options
|
|
117
|
+
* @param options.data - The file data as ArrayBuffer, Blob, or base64 string
|
|
118
|
+
* @param options.fileExtension - File extension (e.g., 'jpg', 'pdf')
|
|
119
|
+
* @param options.mediaType - MIME type of the file (e.g., 'image/jpeg')
|
|
120
|
+
* @param options.metaData - Optional metadata to associate with the attachment
|
|
121
|
+
* @param options.id - Optional custom ID. If not provided, a UUID will be generated
|
|
122
|
+
* @param options.updateHook - Optional callback to execute additional database operations
|
|
123
|
+
* within the same transaction as the attachment creation
|
|
124
|
+
* @returns Promise resolving to the created attachment record
|
|
125
|
+
*/
|
|
126
|
+
saveFile({ data, fileExtension, mediaType, metaData, id, updateHook }: {
|
|
127
|
+
data: AttachmentData;
|
|
128
|
+
fileExtension: string;
|
|
129
|
+
mediaType?: string;
|
|
130
|
+
metaData?: string;
|
|
131
|
+
id?: string;
|
|
132
|
+
updateHook?: (transaction: Transaction, attachment: AttachmentRecord) => Promise<void>;
|
|
133
|
+
}): Promise<AttachmentRecord>;
|
|
134
|
+
deleteFile({ id, updateHook }: {
|
|
135
|
+
id: string;
|
|
136
|
+
updateHook?: (transaction: Transaction, attachment: AttachmentRecord) => Promise<void>;
|
|
137
|
+
}): Promise<void>;
|
|
138
|
+
expireCache(): Promise<void>;
|
|
139
|
+
clearQueue(): Promise<void>;
|
|
140
|
+
/**
|
|
141
|
+
* Verifies the integrity of all attachment records and repairs inconsistencies.
|
|
142
|
+
*
|
|
143
|
+
* This method checks each attachment record against the local filesystem and:
|
|
144
|
+
* - Updates localUri if the file exists at a different path
|
|
145
|
+
* - Archives attachments with missing local files that haven't been uploaded
|
|
146
|
+
* - Requeues synced attachments for download if their local files are missing
|
|
147
|
+
*/
|
|
148
|
+
verifyAttachments(): Promise<void>;
|
|
149
|
+
}
|