@protontech/drive-sdk 0.15.0 → 0.15.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/crypto/driveCrypto.d.ts +1 -1
- package/dist/crypto/driveCrypto.js +2 -2
- package/dist/crypto/driveCrypto.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/internal/apiService/apiService.d.ts +1 -1
- package/dist/internal/apiService/apiService.js +22 -7
- package/dist/internal/apiService/apiService.js.map +1 -1
- package/dist/internal/apiService/apiService.test.js +13 -0
- package/dist/internal/apiService/apiService.test.js.map +1 -1
- package/dist/internal/errors.js +35 -2
- package/dist/internal/errors.js.map +1 -1
- package/dist/internal/events/apiService.d.ts +4 -2
- package/dist/internal/events/apiService.js +17 -13
- package/dist/internal/events/apiService.js.map +1 -1
- package/dist/internal/events/index.d.ts +12 -1
- package/dist/internal/events/index.js +17 -1
- package/dist/internal/events/index.js.map +1 -1
- package/dist/internal/events/index.test.d.ts +1 -0
- package/dist/internal/events/index.test.js +58 -0
- package/dist/internal/events/index.test.js.map +1 -0
- package/dist/internal/nodes/cryptoService.d.ts +1 -0
- package/dist/internal/nodes/cryptoService.js +4 -0
- package/dist/internal/nodes/cryptoService.js.map +1 -1
- package/dist/internal/nodes/nodesAccess.test.js +2 -2
- package/dist/internal/nodes/nodesAccess.test.js.map +1 -1
- package/dist/internal/nodes/nodesManagement.js +2 -2
- package/dist/internal/nodes/nodesManagement.js.map +1 -1
- package/dist/internal/nodes/nodesManagement.test.js +1 -1
- package/dist/internal/nodes/nodesManagement.test.js.map +1 -1
- package/dist/internal/nodes/nodesRevisions.d.ts +1 -1
- package/dist/internal/nodes/nodesRevisions.js +3 -0
- package/dist/internal/nodes/nodesRevisions.js.map +1 -1
- package/dist/internal/nodes/validations.d.ts +1 -1
- package/dist/internal/nodes/validations.js +1 -4
- package/dist/internal/nodes/validations.js.map +1 -1
- package/dist/internal/photos/addToAlbum.js +1 -1
- package/dist/internal/photos/addToAlbum.js.map +1 -1
- package/dist/internal/photos/addToAlbum.test.js +12 -12
- package/dist/internal/photos/addToAlbum.test.js.map +1 -1
- package/dist/internal/photos/albumsManager.js +1 -1
- package/dist/internal/photos/albumsManager.js.map +1 -1
- package/dist/internal/photos/albumsManager.test.js +2 -2
- package/dist/internal/photos/albumsManager.test.js.map +1 -1
- package/dist/internal/photos/apiService.d.ts +3 -3
- package/dist/internal/photos/apiService.js +5 -5
- package/dist/internal/photos/apiService.js.map +1 -1
- package/dist/internal/photos/apiService.test.js +4 -4
- package/dist/internal/photos/apiService.test.js.map +1 -1
- package/dist/internal/photos/photosManager.d.ts +1 -0
- package/dist/internal/photos/photosManager.js +38 -2
- package/dist/internal/photos/photosManager.js.map +1 -1
- package/dist/internal/photos/photosManager.test.js +26 -0
- package/dist/internal/photos/photosManager.test.js.map +1 -1
- package/dist/internal/sharing/cryptoService.js +4 -3
- package/dist/internal/sharing/cryptoService.js.map +1 -1
- package/dist/internal/sharing/cryptoService.test.js +3 -3
- package/dist/internal/sharing/cryptoService.test.js.map +1 -1
- package/dist/internal/sharingPublic/nodes.d.ts +1 -0
- package/dist/internal/sharingPublic/nodes.js +2 -0
- package/dist/internal/sharingPublic/nodes.js.map +1 -1
- package/dist/protonDriveClient.d.ts +14 -2
- package/dist/protonDriveClient.js +6 -0
- package/dist/protonDriveClient.js.map +1 -1
- package/dist/protonDrivePhotosClient.d.ts +10 -2
- package/dist/protonDrivePhotosClient.js +6 -0
- package/dist/protonDrivePhotosClient.js.map +1 -1
- package/package.json +1 -1
- package/src/crypto/driveCrypto.ts +2 -1
- package/src/index.ts +1 -1
- package/src/internal/apiService/apiService.test.ts +16 -0
- package/src/internal/apiService/apiService.ts +20 -2
- package/src/internal/errors.ts +40 -1
- package/src/internal/events/apiService.ts +20 -17
- package/src/internal/events/index.test.ts +67 -0
- package/src/internal/events/index.ts +20 -2
- package/src/internal/nodes/cryptoService.ts +6 -0
- package/src/internal/nodes/nodesAccess.test.ts +2 -2
- package/src/internal/nodes/nodesManagement.test.ts +1 -1
- package/src/internal/nodes/nodesManagement.ts +2 -2
- package/src/internal/nodes/nodesRevisions.ts +5 -1
- package/src/internal/nodes/validations.ts +1 -4
- package/src/internal/photos/addToAlbum.test.ts +12 -12
- package/src/internal/photos/addToAlbum.ts +1 -1
- package/src/internal/photos/albumsManager.test.ts +2 -2
- package/src/internal/photos/albumsManager.ts +1 -1
- package/src/internal/photos/apiService.test.ts +4 -4
- package/src/internal/photos/apiService.ts +6 -6
- package/src/internal/photos/photosManager.test.ts +36 -1
- package/src/internal/photos/photosManager.ts +48 -7
- package/src/internal/sharing/cryptoService.test.ts +3 -3
- package/src/internal/sharing/cryptoService.ts +4 -3
- package/src/internal/sharingPublic/nodes.ts +3 -0
- package/src/protonDriveClient.ts +18 -1
- package/src/protonDrivePhotosClient.ts +15 -5
|
@@ -58,7 +58,7 @@ async function collectSaveToTimelineResults(manager: PhotosManager, nodeUids: st
|
|
|
58
58
|
describe('PhotosManager', () => {
|
|
59
59
|
let logger: ReturnType<typeof getMockLogger>;
|
|
60
60
|
let apiService: jest.Mocked<
|
|
61
|
-
Pick<PhotosAPIService, 'addPhotoTags' | 'removePhotoTags' | 'setPhotoFavorite' | 'transferPhotos'>
|
|
61
|
+
Pick<PhotosAPIService, 'addPhotoTags' | 'removePhotoTags' | 'setPhotoFavorite' | 'transferPhotos' | 'copyPhoto'>
|
|
62
62
|
>;
|
|
63
63
|
let cryptoService: jest.Mocked<Pick<AlbumsCryptoService, 'encryptPhotoForAlbum'>>;
|
|
64
64
|
let nodesService: jest.Mocked<
|
|
@@ -70,6 +70,7 @@ describe('PhotosManager', () => {
|
|
|
70
70
|
| 'iterateNodes'
|
|
71
71
|
| 'getNodePrivateAndSessionKeys'
|
|
72
72
|
| 'notifyNodeChanged'
|
|
73
|
+
| 'notifyChildCreated'
|
|
73
74
|
>
|
|
74
75
|
>;
|
|
75
76
|
let manager: PhotosManager;
|
|
@@ -92,6 +93,7 @@ describe('PhotosManager', () => {
|
|
|
92
93
|
removePhotoTags: jest.fn().mockResolvedValue(undefined),
|
|
93
94
|
setPhotoFavorite: jest.fn().mockResolvedValue(undefined),
|
|
94
95
|
transferPhotos: jest.fn().mockImplementation(async function* () {}),
|
|
96
|
+
copyPhoto: jest.fn().mockResolvedValue('volume1~newPhoto'),
|
|
95
97
|
};
|
|
96
98
|
|
|
97
99
|
cryptoService = {
|
|
@@ -122,6 +124,7 @@ describe('PhotosManager', () => {
|
|
|
122
124
|
passphraseSessionKey: 'passphraseSessionKey' as any,
|
|
123
125
|
}),
|
|
124
126
|
notifyNodeChanged: jest.fn().mockResolvedValue(undefined),
|
|
127
|
+
notifyChildCreated: jest.fn().mockResolvedValue(undefined),
|
|
125
128
|
};
|
|
126
129
|
|
|
127
130
|
manager = new PhotosManager(logger, apiService as any, cryptoService as any, nodesService as any);
|
|
@@ -304,5 +307,37 @@ describe('PhotosManager', () => {
|
|
|
304
307
|
);
|
|
305
308
|
expect(nodesService.notifyNodeChanged).toHaveBeenCalledWith('volume1~photo1');
|
|
306
309
|
});
|
|
310
|
+
|
|
311
|
+
it('copies cross-volume photo and notifies parent root folder', async () => {
|
|
312
|
+
apiService.copyPhoto.mockResolvedValue('volume1~newPhoto1');
|
|
313
|
+
|
|
314
|
+
const results = await collectSaveToTimelineResults(manager, ['volume2~photo1']);
|
|
315
|
+
|
|
316
|
+
expect(results).toEqual([{ uid: 'volume2~photo1', ok: true }]);
|
|
317
|
+
expect(apiService.copyPhoto).toHaveBeenCalledTimes(1);
|
|
318
|
+
expect(nodesService.notifyChildCreated).toHaveBeenCalledWith('volume1~root');
|
|
319
|
+
expect(nodesService.notifyNodeChanged).not.toHaveBeenCalled();
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it('re-queues cross-volume photo once on MissingRelatedPhotosError then succeeds', async () => {
|
|
323
|
+
const missingRelatedUid = 'volume2~related1';
|
|
324
|
+
let copyCall = 0;
|
|
325
|
+
apiService.copyPhoto.mockImplementation(async () => {
|
|
326
|
+
copyCall++;
|
|
327
|
+
if (copyCall === 1) {
|
|
328
|
+
throw new MissingRelatedPhotosError([missingRelatedUid]);
|
|
329
|
+
}
|
|
330
|
+
return 'volume1~newPhoto1';
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
const results = await collectSaveToTimelineResults(manager, ['volume2~photo1']);
|
|
334
|
+
|
|
335
|
+
expect(results).toEqual([{ uid: 'volume2~photo1', ok: true }]);
|
|
336
|
+
expect(apiService.copyPhoto).toHaveBeenCalledTimes(2);
|
|
337
|
+
expect(logger.info).toHaveBeenCalledWith(
|
|
338
|
+
`Missing related photos for saving volume2~photo1, re-queuing: ${missingRelatedUid}`,
|
|
339
|
+
);
|
|
340
|
+
expect(nodesService.notifyChildCreated).toHaveBeenCalledWith('volume1~root');
|
|
341
|
+
});
|
|
307
342
|
});
|
|
308
343
|
});
|
|
@@ -3,12 +3,17 @@ import { c } from 'ttag';
|
|
|
3
3
|
import { AbortError } from '../../errors';
|
|
4
4
|
import { Logger, NodeResultWithError, PhotoTag } from '../../interface';
|
|
5
5
|
import { batch } from '../batch';
|
|
6
|
+
import { splitNodeUid } from '../uids';
|
|
6
7
|
import { createBatches } from './addToAlbum';
|
|
7
8
|
import { AlbumsCryptoService } from './albumsCrypto';
|
|
8
9
|
import { PhotosAPIService } from './apiService';
|
|
9
10
|
import { MissingRelatedPhotosError } from './errors';
|
|
10
11
|
import { PhotosNodesAccess } from './nodes';
|
|
11
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
PhotoAlreadyInTargetError,
|
|
14
|
+
PhotoTransferPayloadBuilder,
|
|
15
|
+
TransferEncryptedPhotoPayload,
|
|
16
|
+
} from './photosTransferPayloadBuilder';
|
|
12
17
|
|
|
13
18
|
/**
|
|
14
19
|
* The number of photos that are loaded in parallel to prepare the payloads.
|
|
@@ -39,13 +44,14 @@ export class PhotosManager {
|
|
|
39
44
|
|
|
40
45
|
async *saveToTimeline(nodeUids: string[], signal?: AbortSignal): AsyncGenerator<NodeResultWithError> {
|
|
41
46
|
const rootNode = await this.nodesService.getVolumeRootFolder();
|
|
47
|
+
const { volumeId: userVolumeId } = splitNodeUid(rootNode.uid);
|
|
42
48
|
const volumeRootKeys = await this.nodesService.getNodeKeys(rootNode.uid);
|
|
43
49
|
const signingKeys = await this.nodesService.getNodeSigningKeys({ nodeUid: rootNode.uid });
|
|
44
50
|
|
|
45
|
-
const queue: {
|
|
46
|
-
photoNodeUid:
|
|
47
|
-
additionalRelatedPhotoNodeUids:
|
|
48
|
-
}
|
|
51
|
+
const queue: { photoNodeUid: string; additionalRelatedPhotoNodeUids: string[] }[] = nodeUids.map((nodeUid) => ({
|
|
52
|
+
photoNodeUid: nodeUid,
|
|
53
|
+
additionalRelatedPhotoNodeUids: [],
|
|
54
|
+
}));
|
|
49
55
|
const retriedPhotoUids = new Set<string>();
|
|
50
56
|
|
|
51
57
|
while (queue.length > 0) {
|
|
@@ -62,7 +68,10 @@ export class PhotosManager {
|
|
|
62
68
|
yield { uid, ok: false, error };
|
|
63
69
|
}
|
|
64
70
|
|
|
65
|
-
|
|
71
|
+
const sameVolumePayloads = payloads.filter((p) => splitNodeUid(p.nodeUid).volumeId === userVolumeId);
|
|
72
|
+
const crossVolumePayloads = payloads.filter((p) => splitNodeUid(p.nodeUid).volumeId !== userVolumeId);
|
|
73
|
+
|
|
74
|
+
for (const batch of createBatches(sameVolumePayloads)) {
|
|
66
75
|
for await (const result of this.apiService.transferPhotos(rootNode.uid, batch, signal)) {
|
|
67
76
|
if (
|
|
68
77
|
!result.ok &&
|
|
@@ -79,16 +88,48 @@ export class PhotosManager {
|
|
|
79
88
|
});
|
|
80
89
|
continue;
|
|
81
90
|
}
|
|
82
|
-
|
|
83
91
|
if (result.ok) {
|
|
84
92
|
await this.nodesService.notifyNodeChanged(result.uid);
|
|
85
93
|
}
|
|
86
94
|
yield result;
|
|
87
95
|
}
|
|
88
96
|
}
|
|
97
|
+
|
|
98
|
+
// Cross-volume photos (e.g. from shared-with-me albums): copy into the user's own
|
|
99
|
+
// timeline root using the generic copy endpoint.
|
|
100
|
+
for (const payload of crossVolumePayloads) {
|
|
101
|
+
try {
|
|
102
|
+
await this.copyPhoto(payload, signal);
|
|
103
|
+
await this.nodesService.notifyChildCreated(rootNode.uid);
|
|
104
|
+
yield { uid: payload.nodeUid, ok: true };
|
|
105
|
+
} catch (error) {
|
|
106
|
+
if (error instanceof MissingRelatedPhotosError && !retriedPhotoUids.has(payload.nodeUid)) {
|
|
107
|
+
retriedPhotoUids.add(payload.nodeUid);
|
|
108
|
+
this.logger.info(
|
|
109
|
+
`Missing related photos for saving ${payload.nodeUid}, re-queuing: ${error.missingNodeUids.join(', ')}`,
|
|
110
|
+
);
|
|
111
|
+
queue.push({
|
|
112
|
+
photoNodeUid: payload.nodeUid,
|
|
113
|
+
additionalRelatedPhotoNodeUids: error.missingNodeUids,
|
|
114
|
+
});
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
yield {
|
|
118
|
+
uid: payload.nodeUid,
|
|
119
|
+
ok: false,
|
|
120
|
+
error:
|
|
121
|
+
error instanceof Error ? error : new Error(c('Error').t`Unknown error`, { cause: error }),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|
|
89
125
|
}
|
|
90
126
|
}
|
|
91
127
|
|
|
128
|
+
private async copyPhoto(payload: TransferEncryptedPhotoPayload, signal?: AbortSignal): Promise<string> {
|
|
129
|
+
const rootNode = await this.nodesService.getVolumeRootFolder();
|
|
130
|
+
return this.apiService.copyPhoto(rootNode.uid, payload, signal);
|
|
131
|
+
}
|
|
132
|
+
|
|
92
133
|
async *updatePhotos(photos: UpdatePhotoSettings[], signal?: AbortSignal): AsyncGenerator<NodeResultWithError> {
|
|
93
134
|
for await (const {
|
|
94
135
|
photoSettings: { nodeUid, tagsToAdd, tagsToRemove },
|
|
@@ -169,7 +169,7 @@ describe('SharingCryptoService', () => {
|
|
|
169
169
|
|
|
170
170
|
it('should handle invalid node name', async () => {
|
|
171
171
|
driveCrypto.decryptNodeName = jest.fn().mockResolvedValue({
|
|
172
|
-
name: '
|
|
172
|
+
name: '',
|
|
173
173
|
});
|
|
174
174
|
|
|
175
175
|
const result = await cryptoService.decryptBookmark(encryptedBookmark);
|
|
@@ -177,8 +177,8 @@ describe('SharingCryptoService', () => {
|
|
|
177
177
|
expect(result).toMatchObject({
|
|
178
178
|
url: resultOk('https://drive.proton.me/urls/tokenId#urlPassword'),
|
|
179
179
|
nodeName: resultError({
|
|
180
|
-
name: '
|
|
181
|
-
error: "Name must not
|
|
180
|
+
name: '',
|
|
181
|
+
error: "Name must not be empty",
|
|
182
182
|
}),
|
|
183
183
|
});
|
|
184
184
|
});
|
|
@@ -193,12 +193,12 @@ export class SharingCryptoService {
|
|
|
193
193
|
encryptedInvitation: EncryptedInvitationWithNode,
|
|
194
194
|
): Promise<ProtonInvitationWithNode> {
|
|
195
195
|
const inviteeAddress = await this.account.getOwnAddress(encryptedInvitation.inviteeEmail);
|
|
196
|
-
const
|
|
196
|
+
const inviteeKeys = inviteeAddress.keys.map(k => k.key);
|
|
197
197
|
|
|
198
198
|
const shareKey = await this.driveCrypto.decryptUnsignedKey(
|
|
199
199
|
encryptedInvitation.share.armoredKey,
|
|
200
200
|
encryptedInvitation.share.armoredPassphrase,
|
|
201
|
-
|
|
201
|
+
inviteeKeys,
|
|
202
202
|
);
|
|
203
203
|
|
|
204
204
|
let nodeName: Result<string, Error>;
|
|
@@ -246,7 +246,8 @@ export class SharingCryptoService {
|
|
|
246
246
|
}> {
|
|
247
247
|
const inviteeAddress = await this.account.getOwnAddress(encryptedInvitation.inviteeEmail);
|
|
248
248
|
const inviteeKey = inviteeAddress.keys[inviteeAddress.primaryKeyIndex].key;
|
|
249
|
-
const
|
|
249
|
+
const inviteeKeys = inviteeAddress.keys.map(k => k.key);
|
|
250
|
+
const result = await this.driveCrypto.acceptInvitation(encryptedInvitation.base64KeyPacket, inviteeKeys, inviteeKey);
|
|
250
251
|
return result;
|
|
251
252
|
}
|
|
252
253
|
|
|
@@ -17,6 +17,9 @@ import { makeNodeUid, splitNodeUid } from '../uids';
|
|
|
17
17
|
import { SharingPublicSharesManager } from './shares';
|
|
18
18
|
|
|
19
19
|
export class SharingPublicNodesCryptoService extends NodesCryptoService {
|
|
20
|
+
// Do not allow fallback verification for public links, because it is not possible to load owners' address keys.
|
|
21
|
+
protected allowContentKeyPacketFallbackVerification = false;
|
|
22
|
+
|
|
20
23
|
async generateDocument(
|
|
21
24
|
parentKeys: { key: PrivateKey; hashKey: Uint8Array<ArrayBuffer> },
|
|
22
25
|
signingKeys: NodeSigningKeys,
|
package/src/protonDriveClient.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
Device,
|
|
7
7
|
DeviceOrUid,
|
|
8
8
|
DeviceType,
|
|
9
|
+
DriveEvent,
|
|
9
10
|
FileDownloader,
|
|
10
11
|
FileUploader,
|
|
11
12
|
Logger,
|
|
@@ -36,7 +37,7 @@ import {
|
|
|
36
37
|
import { DriveAPIService } from './internal/apiService';
|
|
37
38
|
import { initDevicesModule } from './internal/devices';
|
|
38
39
|
import { initDownloadModule } from './internal/download';
|
|
39
|
-
import { DriveEventsService, DriveListener, EventSubscription } from './internal/events';
|
|
40
|
+
import { CoreApiEvent, DriveEventsService, DriveListener, EventSubscription } from './internal/events';
|
|
40
41
|
import { initNodesModule } from './internal/nodes';
|
|
41
42
|
import { SDKEvents } from './internal/sdkEvents';
|
|
42
43
|
import { initSharesModule } from './internal/shares';
|
|
@@ -113,6 +114,16 @@ export class ProtonDriveClient {
|
|
|
113
114
|
customPassword?: string,
|
|
114
115
|
isAnonymousContext?: boolean,
|
|
115
116
|
) => Promise<ProtonDrivePublicLinkClient>;
|
|
117
|
+
/**
|
|
118
|
+
* Feed a raw core API event response into the SDK.
|
|
119
|
+
*
|
|
120
|
+
* The SDK will derive drive-relevant events (e.g. `SharedWithMeUpdated`)
|
|
121
|
+
* from it, update internal caches, and return the derived events.
|
|
122
|
+
*
|
|
123
|
+
* The `rawEvent` shape matches the response of the
|
|
124
|
+
* `core/v5/events/{id}` endpoint.
|
|
125
|
+
*/
|
|
126
|
+
processCoreEvent: (rawEvent: CoreApiEvent) => Promise<DriveEvent[]>;
|
|
116
127
|
};
|
|
117
128
|
|
|
118
129
|
constructor({
|
|
@@ -255,6 +266,10 @@ export class ProtonDriveClient {
|
|
|
255
266
|
session,
|
|
256
267
|
});
|
|
257
268
|
},
|
|
269
|
+
processCoreEvent: async (rawEvent: CoreApiEvent) => {
|
|
270
|
+
this.logger.debug(`Processing core event ${rawEvent.EventID}`);
|
|
271
|
+
return this.events.processCoreEvent(rawEvent);
|
|
272
|
+
},
|
|
258
273
|
};
|
|
259
274
|
}
|
|
260
275
|
|
|
@@ -293,6 +308,8 @@ export class ProtonDriveClient {
|
|
|
293
308
|
* Subscribes to the remote general data updates.
|
|
294
309
|
*
|
|
295
310
|
* Only one instance of the SDK should subscribe to updates.
|
|
311
|
+
*
|
|
312
|
+
* @deprecated Use `experimental.processCoreEvent` instead.
|
|
296
313
|
*/
|
|
297
314
|
async subscribeToDriveEvents(callback: DriveListener): Promise<EventSubscription> {
|
|
298
315
|
this.logger.debug('Subscribing to core updates');
|
|
@@ -2,6 +2,7 @@ import { getConfig } from './config';
|
|
|
2
2
|
import { DriveCrypto } from './crypto';
|
|
3
3
|
import { NullFeatureFlagProvider } from './featureFlags';
|
|
4
4
|
import {
|
|
5
|
+
DriveEvent,
|
|
5
6
|
FileDownloader,
|
|
6
7
|
FileUploader,
|
|
7
8
|
Logger,
|
|
@@ -26,7 +27,7 @@ import {
|
|
|
26
27
|
} from './interface';
|
|
27
28
|
import { DriveAPIService } from './internal/apiService';
|
|
28
29
|
import { initDownloadModule } from './internal/download';
|
|
29
|
-
import { DriveEventsService, DriveListener, EventSubscription } from './internal/events';
|
|
30
|
+
import { CoreApiEvent, DriveEventsService, DriveListener, EventSubscription } from './internal/events';
|
|
30
31
|
import {
|
|
31
32
|
AlbumItem,
|
|
32
33
|
initPhotoSharesModule,
|
|
@@ -82,6 +83,12 @@ export class ProtonDrivePhotosClient {
|
|
|
82
83
|
* @param signal - An optional abort signal to cancel the operation.
|
|
83
84
|
*/
|
|
84
85
|
iterateAlbumUids: (signal?: AbortSignal) => AsyncGenerator<string>;
|
|
86
|
+
/**
|
|
87
|
+
* Feed a raw core API event response into the SDK.
|
|
88
|
+
*
|
|
89
|
+
* See `ProtonDriveClient.experimental.processCoreEvent` for more information.
|
|
90
|
+
*/
|
|
91
|
+
processCoreEvent: (rawEvent: CoreApiEvent) => Promise<DriveEvent[]>;
|
|
85
92
|
};
|
|
86
93
|
|
|
87
94
|
constructor({
|
|
@@ -186,6 +193,10 @@ export class ProtonDrivePhotosClient {
|
|
|
186
193
|
this.logger.debug('Iterating album UIDs');
|
|
187
194
|
return this.photos.albums.iterateAlbumUids(signal);
|
|
188
195
|
},
|
|
196
|
+
processCoreEvent: async (rawEvent: CoreApiEvent) => {
|
|
197
|
+
this.logger.debug(`Processing core event ${rawEvent.EventID}`);
|
|
198
|
+
return this.events.processCoreEvent(rawEvent);
|
|
199
|
+
},
|
|
189
200
|
};
|
|
190
201
|
}
|
|
191
202
|
|
|
@@ -213,6 +224,8 @@ export class ProtonDrivePhotosClient {
|
|
|
213
224
|
* Subscribes to the remote general data updates.
|
|
214
225
|
*
|
|
215
226
|
* See `ProtonDriveClient.subscribeToDriveEvents` for more information.
|
|
227
|
+
*
|
|
228
|
+
* @deprecated Use `experimental.processCoreEvent` instead.
|
|
216
229
|
*/
|
|
217
230
|
async subscribeToDriveEvents(callback: DriveListener): Promise<EventSubscription> {
|
|
218
231
|
this.logger.debug('Subscribing to core updates');
|
|
@@ -707,10 +720,7 @@ export class ProtonDrivePhotosClient {
|
|
|
707
720
|
* @param signal - An optional abort signal to cancel the operation.
|
|
708
721
|
* @returns An async generator of per-photo results.
|
|
709
722
|
*/
|
|
710
|
-
async *savePhotosToTimeline(
|
|
711
|
-
photoNodeUids: NodeOrUid[],
|
|
712
|
-
signal?: AbortSignal,
|
|
713
|
-
): AsyncGenerator<NodeResultWithError> {
|
|
723
|
+
async *savePhotosToTimeline(photoNodeUids: NodeOrUid[], signal?: AbortSignal): AsyncGenerator<NodeResultWithError> {
|
|
714
724
|
this.logger.info(`Saving ${photoNodeUids.length} photos to timeline`);
|
|
715
725
|
yield* this.photos.photos.saveToTimeline(getUids(photoNodeUids), signal);
|
|
716
726
|
}
|