@syncvault/sdk 1.2.0 → 1.3.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/package.json +1 -1
- package/src/index.d.ts +23 -1
- package/src/index.js +68 -2
- package/src/offline.js +30 -7
package/package.json
CHANGED
package/src/index.d.ts
CHANGED
|
@@ -35,6 +35,20 @@ export interface QuotaInfo {
|
|
|
35
35
|
unlimited: boolean;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
export interface SharedVault {
|
|
39
|
+
id: string;
|
|
40
|
+
name: string;
|
|
41
|
+
ownerId: string;
|
|
42
|
+
ownerUsername: string;
|
|
43
|
+
memberCount: number;
|
|
44
|
+
isOwner: boolean;
|
|
45
|
+
createdAt: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface PutOptions {
|
|
49
|
+
updatedAt?: number | Date;
|
|
50
|
+
}
|
|
51
|
+
|
|
38
52
|
export declare class SyncVault {
|
|
39
53
|
constructor(options: SyncVaultOptions);
|
|
40
54
|
|
|
@@ -48,7 +62,7 @@ export declare class SyncVault {
|
|
|
48
62
|
register(username: string, password: string): Promise<User>;
|
|
49
63
|
|
|
50
64
|
// Data operations
|
|
51
|
-
put<T = unknown>(path: string, data: T): Promise<PutResponse>;
|
|
65
|
+
put<T = unknown>(path: string, data: T, options?: PutOptions): Promise<PutResponse>;
|
|
52
66
|
get<T = unknown>(path: string): Promise<T>;
|
|
53
67
|
list(): Promise<FileInfo[]>;
|
|
54
68
|
delete(path: string): Promise<DeleteResponse>;
|
|
@@ -64,6 +78,13 @@ export declare class SyncVault {
|
|
|
64
78
|
// Quota info
|
|
65
79
|
getQuota(): Promise<QuotaInfo>;
|
|
66
80
|
|
|
81
|
+
// Shared vaults
|
|
82
|
+
getSharedVaults(): Promise<SharedVault[]>;
|
|
83
|
+
listShared(vaultId: string): Promise<FileInfo[]>;
|
|
84
|
+
putShared<T = unknown>(vaultId: string, path: string, data: T, sharedPassword?: string): Promise<PutResponse>;
|
|
85
|
+
getShared<T = unknown>(vaultId: string, path: string, sharedPassword?: string): Promise<T>;
|
|
86
|
+
deleteShared(vaultId: string, path: string): Promise<DeleteResponse>;
|
|
87
|
+
|
|
67
88
|
// State
|
|
68
89
|
isAuthenticated(): boolean;
|
|
69
90
|
logout(): void;
|
|
@@ -79,6 +100,7 @@ export interface PendingOperation {
|
|
|
79
100
|
type: 'put' | 'delete';
|
|
80
101
|
path: string;
|
|
81
102
|
data?: string;
|
|
103
|
+
updatedAt?: number;
|
|
82
104
|
createdAt: number;
|
|
83
105
|
retries: number;
|
|
84
106
|
}
|
package/src/index.js
CHANGED
|
@@ -107,15 +107,24 @@ export class SyncVault {
|
|
|
107
107
|
|
|
108
108
|
/**
|
|
109
109
|
* Store encrypted data
|
|
110
|
+
* @param {string} path - File path
|
|
111
|
+
* @param {any} data - Data to encrypt and store
|
|
112
|
+
* @param {Object} options - Optional settings
|
|
113
|
+
* @param {number} options.updatedAt - Timestamp for LWW conflict resolution
|
|
110
114
|
*/
|
|
111
|
-
async put(path, data) {
|
|
115
|
+
async put(path, data, options = {}) {
|
|
112
116
|
this._checkAuth();
|
|
113
117
|
|
|
114
118
|
const encrypted = await encrypt(data, this.password);
|
|
119
|
+
const body = { path, data: encrypted };
|
|
120
|
+
|
|
121
|
+
if (options.updatedAt) {
|
|
122
|
+
body.updatedAt = new Date(options.updatedAt).toISOString();
|
|
123
|
+
}
|
|
115
124
|
|
|
116
125
|
const response = await this._request('/api/sync/put', {
|
|
117
126
|
method: 'POST',
|
|
118
|
-
body: JSON.stringify(
|
|
127
|
+
body: JSON.stringify(body)
|
|
119
128
|
});
|
|
120
129
|
|
|
121
130
|
return response;
|
|
@@ -215,6 +224,63 @@ export class SyncVault {
|
|
|
215
224
|
return this._request('/api/sync/quota');
|
|
216
225
|
}
|
|
217
226
|
|
|
227
|
+
// --- Shared Vaults ---
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Get all shared vaults the user has access to in this app
|
|
231
|
+
*/
|
|
232
|
+
async getSharedVaults() {
|
|
233
|
+
this._checkAuth();
|
|
234
|
+
|
|
235
|
+
return this._request('/api/sync/shared/vaults');
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* List files in a shared vault
|
|
240
|
+
*/
|
|
241
|
+
async listShared(vaultId) {
|
|
242
|
+
this._checkAuth();
|
|
243
|
+
|
|
244
|
+
const response = await this._request(`/api/sync/shared/${vaultId}/list`);
|
|
245
|
+
return response.files;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Store encrypted data in a shared vault
|
|
250
|
+
*/
|
|
251
|
+
async putShared(vaultId, path, data, sharedPassword) {
|
|
252
|
+
this._checkAuth();
|
|
253
|
+
|
|
254
|
+
const encrypted = await encrypt(data, sharedPassword || this.password);
|
|
255
|
+
|
|
256
|
+
return this._request(`/api/sync/shared/${vaultId}/put`, {
|
|
257
|
+
method: 'POST',
|
|
258
|
+
body: JSON.stringify({ path, data: encrypted })
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Retrieve and decrypt data from a shared vault
|
|
264
|
+
*/
|
|
265
|
+
async getShared(vaultId, path, sharedPassword) {
|
|
266
|
+
this._checkAuth();
|
|
267
|
+
|
|
268
|
+
const response = await this._request(`/api/sync/shared/${vaultId}/get?path=${encodeURIComponent(path)}`);
|
|
269
|
+
return decrypt(response.data, sharedPassword || this.password);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Delete a file from a shared vault
|
|
274
|
+
*/
|
|
275
|
+
async deleteShared(vaultId, path) {
|
|
276
|
+
this._checkAuth();
|
|
277
|
+
|
|
278
|
+
return this._request(`/api/sync/shared/${vaultId}/delete`, {
|
|
279
|
+
method: 'POST',
|
|
280
|
+
body: JSON.stringify({ path })
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
218
284
|
/**
|
|
219
285
|
* Check if user is authenticated
|
|
220
286
|
*/
|
package/src/offline.js
CHANGED
|
@@ -219,15 +219,16 @@ export class OfflineSyncVault {
|
|
|
219
219
|
}
|
|
220
220
|
|
|
221
221
|
/**
|
|
222
|
-
* Put with offline support
|
|
222
|
+
* Put with offline support (LWW enabled)
|
|
223
223
|
*/
|
|
224
224
|
async put(path, data) {
|
|
225
225
|
await this.init();
|
|
226
226
|
|
|
227
|
+
const timestamp = Date.now();
|
|
227
228
|
const encrypted = await encrypt(data, this.client.password);
|
|
228
229
|
|
|
229
230
|
try {
|
|
230
|
-
const result = await this.client.put(path, data);
|
|
231
|
+
const result = await this.client.put(path, data, { updatedAt: timestamp });
|
|
231
232
|
await this.store.setCache(path, encrypted);
|
|
232
233
|
return result;
|
|
233
234
|
} catch (error) {
|
|
@@ -236,7 +237,8 @@ export class OfflineSyncVault {
|
|
|
236
237
|
await this.store.queueOperation({
|
|
237
238
|
type: 'put',
|
|
238
239
|
path,
|
|
239
|
-
data: encrypted
|
|
240
|
+
data: encrypted,
|
|
241
|
+
updatedAt: timestamp
|
|
240
242
|
});
|
|
241
243
|
return { queued: true, path };
|
|
242
244
|
}
|
|
@@ -391,10 +393,31 @@ export class OfflineSyncVault {
|
|
|
391
393
|
}
|
|
392
394
|
|
|
393
395
|
async _syncPut(op) {
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
body
|
|
397
|
-
}
|
|
396
|
+
const body = { path: op.path, data: op.data };
|
|
397
|
+
if (op.updatedAt) {
|
|
398
|
+
body.updatedAt = new Date(op.updatedAt).toISOString();
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
try {
|
|
402
|
+
await this.client._request('/api/sync/put', {
|
|
403
|
+
method: 'POST',
|
|
404
|
+
body: JSON.stringify(body)
|
|
405
|
+
});
|
|
406
|
+
} catch (error) {
|
|
407
|
+
// LWW conflict - server has newer data, discard local change
|
|
408
|
+
if (error.statusCode === 409 && error.data?.code === 'CONFLICT_STALE') {
|
|
409
|
+
// Fetch fresh data from server to update cache
|
|
410
|
+
try {
|
|
411
|
+
const result = await this.client.get(op.path);
|
|
412
|
+
const encrypted = await encrypt(result, this.client.password);
|
|
413
|
+
await this.store.setCache(op.path, encrypted);
|
|
414
|
+
} catch (cacheErr) {
|
|
415
|
+
console.warn('Failed to update cache after LWW conflict:', cacheErr.message);
|
|
416
|
+
}
|
|
417
|
+
return; // Consider this "synced" - we accepted server's version
|
|
418
|
+
}
|
|
419
|
+
throw error;
|
|
420
|
+
}
|
|
398
421
|
}
|
|
399
422
|
|
|
400
423
|
async _syncDelete(op) {
|