@protontech/drive-sdk 0.9.8 → 0.10.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.
Files changed (174) hide show
  1. package/dist/crypto/driveCrypto.d.ts +15 -15
  2. package/dist/crypto/driveCrypto.js.map +1 -1
  3. package/dist/crypto/hmac.d.ts +3 -3
  4. package/dist/crypto/hmac.js.map +1 -1
  5. package/dist/crypto/interface.d.ts +45 -25
  6. package/dist/crypto/interface.js.map +1 -1
  7. package/dist/crypto/openPGPCrypto.d.ts +37 -37
  8. package/dist/crypto/openPGPCrypto.js.map +1 -1
  9. package/dist/crypto/utils.d.ts +1 -1
  10. package/dist/interface/index.d.ts +3 -3
  11. package/dist/interface/index.js.map +1 -1
  12. package/dist/interface/nodes.d.ts +8 -0
  13. package/dist/interface/photos.d.ts +18 -1
  14. package/dist/interface/sharing.d.ts +2 -0
  15. package/dist/interface/telemetry.d.ts +1 -0
  16. package/dist/interface/telemetry.js.map +1 -1
  17. package/dist/interface/thumbnail.d.ts +2 -2
  18. package/dist/internal/apiService/apiService.js +25 -12
  19. package/dist/internal/apiService/apiService.js.map +1 -1
  20. package/dist/internal/apiService/apiService.test.js +33 -5
  21. package/dist/internal/apiService/apiService.test.js.map +1 -1
  22. package/dist/internal/apiService/driveTypes.d.ts +2942 -3187
  23. package/dist/internal/apiService/errors.test.js +17 -7
  24. package/dist/internal/apiService/errors.test.js.map +1 -1
  25. package/dist/internal/devices/manager.d.ts +1 -0
  26. package/dist/internal/devices/manager.js +11 -0
  27. package/dist/internal/devices/manager.js.map +1 -1
  28. package/dist/internal/download/apiService.d.ts +1 -1
  29. package/dist/internal/download/cryptoService.d.ts +4 -4
  30. package/dist/internal/download/cryptoService.js.map +1 -1
  31. package/dist/internal/download/fileDownloader.js.map +1 -1
  32. package/dist/internal/download/fileDownloader.test.js.map +1 -1
  33. package/dist/internal/download/thumbnailDownloader.js.map +1 -1
  34. package/dist/internal/nodes/cryptoService.d.ts +4 -4
  35. package/dist/internal/nodes/cryptoService.js +5 -3
  36. package/dist/internal/nodes/cryptoService.js.map +1 -1
  37. package/dist/internal/nodes/cryptoService.test.js.map +1 -1
  38. package/dist/internal/nodes/interface.d.ts +1 -1
  39. package/dist/internal/nodes/nodesManagement.js +0 -1
  40. package/dist/internal/nodes/nodesManagement.js.map +1 -1
  41. package/dist/internal/photos/addToAlbum.d.ts +46 -0
  42. package/dist/internal/photos/addToAlbum.js +257 -0
  43. package/dist/internal/photos/addToAlbum.js.map +1 -0
  44. package/dist/internal/photos/addToAlbum.test.d.ts +1 -0
  45. package/dist/internal/photos/addToAlbum.test.js +409 -0
  46. package/dist/internal/photos/addToAlbum.test.js.map +1 -0
  47. package/dist/internal/photos/albums.d.ts +7 -2
  48. package/dist/internal/photos/albums.js +24 -1
  49. package/dist/internal/photos/albums.js.map +1 -1
  50. package/dist/internal/photos/albums.test.js +26 -1
  51. package/dist/internal/photos/albums.test.js.map +1 -1
  52. package/dist/internal/photos/albumsCrypto.d.ts +20 -3
  53. package/dist/internal/photos/albumsCrypto.js +27 -0
  54. package/dist/internal/photos/albumsCrypto.js.map +1 -1
  55. package/dist/internal/photos/apiService.d.ts +20 -0
  56. package/dist/internal/photos/apiService.js +142 -0
  57. package/dist/internal/photos/apiService.js.map +1 -1
  58. package/dist/internal/photos/apiService.test.d.ts +1 -0
  59. package/dist/internal/photos/apiService.test.js +199 -0
  60. package/dist/internal/photos/apiService.test.js.map +1 -0
  61. package/dist/internal/photos/errors.d.ts +4 -0
  62. package/dist/internal/photos/errors.js +17 -0
  63. package/dist/internal/photos/errors.js.map +1 -0
  64. package/dist/internal/photos/index.d.ts +1 -1
  65. package/dist/internal/photos/index.js +1 -1
  66. package/dist/internal/photos/index.js.map +1 -1
  67. package/dist/internal/photos/interface.d.ts +36 -1
  68. package/dist/internal/photos/interface.js +14 -0
  69. package/dist/internal/photos/interface.js.map +1 -1
  70. package/dist/internal/photos/nodes.js +32 -2
  71. package/dist/internal/photos/nodes.js.map +1 -1
  72. package/dist/internal/photos/nodes.test.js +25 -5
  73. package/dist/internal/photos/nodes.test.js.map +1 -1
  74. package/dist/internal/photos/timeline.d.ts +2 -5
  75. package/dist/internal/photos/timeline.js.map +1 -1
  76. package/dist/internal/photos/upload.d.ts +2 -2
  77. package/dist/internal/photos/upload.js.map +1 -1
  78. package/dist/internal/shares/apiService.js +1 -0
  79. package/dist/internal/shares/apiService.js.map +1 -1
  80. package/dist/internal/shares/interface.d.ts +1 -0
  81. package/dist/internal/sharing/apiService.d.ts +8 -1
  82. package/dist/internal/sharing/apiService.js +23 -1
  83. package/dist/internal/sharing/apiService.js.map +1 -1
  84. package/dist/internal/sharing/cryptoService.js +8 -4
  85. package/dist/internal/sharing/cryptoService.js.map +1 -1
  86. package/dist/internal/sharing/sharingManagement.d.ts +1 -0
  87. package/dist/internal/sharing/sharingManagement.js +15 -2
  88. package/dist/internal/sharing/sharingManagement.js.map +1 -1
  89. package/dist/internal/sharing/sharingManagement.test.js +30 -5
  90. package/dist/internal/sharing/sharingManagement.test.js.map +1 -1
  91. package/dist/internal/sharingPublic/nodes.d.ts +2 -2
  92. package/dist/internal/sharingPublic/nodes.js.map +1 -1
  93. package/dist/internal/upload/apiService.d.ts +5 -5
  94. package/dist/internal/upload/apiService.js.map +1 -1
  95. package/dist/internal/upload/blockVerifier.d.ts +2 -2
  96. package/dist/internal/upload/blockVerifier.js.map +1 -1
  97. package/dist/internal/upload/chunkStreamReader.d.ts +2 -2
  98. package/dist/internal/upload/chunkStreamReader.js.map +1 -1
  99. package/dist/internal/upload/chunkStreamReader.test.js.map +1 -1
  100. package/dist/internal/upload/cryptoService.d.ts +7 -7
  101. package/dist/internal/upload/cryptoService.js.map +1 -1
  102. package/dist/internal/upload/interface.d.ts +6 -6
  103. package/dist/internal/upload/manager.d.ts +1 -1
  104. package/dist/internal/upload/manager.js.map +1 -1
  105. package/dist/internal/upload/streamUploader.d.ts +1 -1
  106. package/dist/internal/utils.d.ts +1 -1
  107. package/dist/protonDriveClient.d.ts +8 -0
  108. package/dist/protonDriveClient.js +11 -0
  109. package/dist/protonDriveClient.js.map +1 -1
  110. package/dist/protonDrivePhotosClient.d.ts +42 -7
  111. package/dist/protonDrivePhotosClient.js +50 -2
  112. package/dist/protonDrivePhotosClient.js.map +1 -1
  113. package/dist/protonDrivePublicLinkClient.d.ts +9 -0
  114. package/dist/protonDrivePublicLinkClient.js +12 -0
  115. package/dist/protonDrivePublicLinkClient.js.map +1 -1
  116. package/dist/transformers.js +2 -0
  117. package/dist/transformers.js.map +1 -1
  118. package/package.json +4 -4
  119. package/src/crypto/driveCrypto.ts +15 -15
  120. package/src/crypto/hmac.ts +4 -4
  121. package/src/crypto/interface.ts +58 -27
  122. package/src/crypto/openPGPCrypto.ts +26 -26
  123. package/src/interface/index.ts +10 -2
  124. package/src/interface/nodes.ts +1 -0
  125. package/src/interface/photos.ts +19 -1
  126. package/src/interface/sharing.ts +2 -0
  127. package/src/interface/telemetry.ts +1 -0
  128. package/src/interface/thumbnail.ts +2 -2
  129. package/src/internal/apiService/apiService.test.ts +38 -6
  130. package/src/internal/apiService/apiService.ts +33 -12
  131. package/src/internal/apiService/driveTypes.ts +2942 -3187
  132. package/src/internal/devices/manager.ts +14 -0
  133. package/src/internal/download/apiService.ts +1 -1
  134. package/src/internal/download/cryptoService.ts +4 -4
  135. package/src/internal/download/fileDownloader.test.ts +4 -4
  136. package/src/internal/download/fileDownloader.ts +6 -6
  137. package/src/internal/download/thumbnailDownloader.ts +4 -4
  138. package/src/internal/nodes/cryptoService.test.ts +2 -2
  139. package/src/internal/nodes/cryptoService.ts +11 -8
  140. package/src/internal/nodes/interface.ts +1 -1
  141. package/src/internal/nodes/nodesManagement.ts +0 -1
  142. package/src/internal/photos/addToAlbum.test.ts +515 -0
  143. package/src/internal/photos/addToAlbum.ts +341 -0
  144. package/src/internal/photos/albums.test.ts +46 -22
  145. package/src/internal/photos/albums.ts +48 -2
  146. package/src/internal/photos/albumsCrypto.ts +54 -3
  147. package/src/internal/photos/apiService.test.ts +233 -0
  148. package/src/internal/photos/apiService.ts +234 -15
  149. package/src/internal/photos/errors.ts +11 -0
  150. package/src/internal/photos/index.ts +2 -2
  151. package/src/internal/photos/interface.ts +40 -1
  152. package/src/internal/photos/nodes.test.ts +27 -6
  153. package/src/internal/photos/nodes.ts +34 -2
  154. package/src/internal/photos/timeline.ts +2 -5
  155. package/src/internal/photos/upload.ts +2 -2
  156. package/src/internal/shares/apiService.ts +1 -0
  157. package/src/internal/shares/interface.ts +1 -0
  158. package/src/internal/sharing/apiService.ts +49 -5
  159. package/src/internal/sharing/cryptoService.ts +10 -4
  160. package/src/internal/sharing/sharingManagement.test.ts +33 -5
  161. package/src/internal/sharing/sharingManagement.ts +28 -6
  162. package/src/internal/sharingPublic/nodes.ts +1 -1
  163. package/src/internal/upload/apiService.ts +5 -5
  164. package/src/internal/upload/blockVerifier.ts +3 -3
  165. package/src/internal/upload/chunkStreamReader.test.ts +7 -7
  166. package/src/internal/upload/chunkStreamReader.ts +3 -3
  167. package/src/internal/upload/cryptoService.ts +9 -9
  168. package/src/internal/upload/interface.ts +6 -6
  169. package/src/internal/upload/manager.ts +2 -2
  170. package/src/internal/upload/streamUploader.ts +1 -1
  171. package/src/protonDriveClient.ts +15 -3
  172. package/src/protonDrivePhotosClient.ts +78 -22
  173. package/src/protonDrivePublicLinkClient.ts +13 -0
  174. package/src/transformers.ts +2 -0
@@ -1,10 +1,10 @@
1
1
  import { ChunkStreamReader } from './chunkStreamReader';
2
2
 
3
3
  describe('ChunkStreamReader', () => {
4
- let stream: ReadableStream<Uint8Array>;
4
+ let stream: ReadableStream<Uint8Array<ArrayBuffer>>;
5
5
 
6
6
  beforeEach(() => {
7
- stream = new ReadableStream<Uint8Array>({
7
+ stream = new ReadableStream<Uint8Array<ArrayBuffer>>({
8
8
  start(controller) {
9
9
  controller.enqueue(new Uint8Array([1, 2, 3]));
10
10
  controller.enqueue(new Uint8Array([4, 5, 6]));
@@ -18,7 +18,7 @@ describe('ChunkStreamReader', () => {
18
18
  it('should yield chunks as enqueued if matching the size', async () => {
19
19
  const reader = new ChunkStreamReader(stream, 3);
20
20
 
21
- const chunks: Uint8Array[] = [];
21
+ const chunks: Uint8Array<ArrayBuffer>[] = [];
22
22
  for await (const chunk of reader.iterateChunks()) {
23
23
  chunks.push(new Uint8Array(chunk));
24
24
  }
@@ -33,7 +33,7 @@ describe('ChunkStreamReader', () => {
33
33
  it('should yield smaller chunks than enqueued chunks', async () => {
34
34
  const reader = new ChunkStreamReader(stream, 2);
35
35
 
36
- const chunks: Uint8Array[] = [];
36
+ const chunks: Uint8Array<ArrayBuffer>[] = [];
37
37
  for await (const chunk of reader.iterateChunks()) {
38
38
  chunks.push(new Uint8Array(chunk));
39
39
  }
@@ -50,7 +50,7 @@ describe('ChunkStreamReader', () => {
50
50
  it('should yield bigger chunks than enqueued chunks', async () => {
51
51
  const reader = new ChunkStreamReader(stream, 4);
52
52
 
53
- const chunks: Uint8Array[] = [];
53
+ const chunks: Uint8Array<ArrayBuffer>[] = [];
54
54
  for await (const chunk of reader.iterateChunks()) {
55
55
  chunks.push(new Uint8Array(chunk));
56
56
  }
@@ -64,7 +64,7 @@ describe('ChunkStreamReader', () => {
64
64
  it('should yield last incomplete chunk', async () => {
65
65
  const reader = new ChunkStreamReader(stream, 5);
66
66
 
67
- const chunks: Uint8Array[] = [];
67
+ const chunks: Uint8Array<ArrayBuffer>[] = [];
68
68
  for await (const chunk of reader.iterateChunks()) {
69
69
  chunks.push(new Uint8Array(chunk));
70
70
  }
@@ -78,7 +78,7 @@ describe('ChunkStreamReader', () => {
78
78
  it('should yield as one big chunk', async () => {
79
79
  const reader = new ChunkStreamReader(stream, 100);
80
80
 
81
- const chunks: Uint8Array[] = [];
81
+ const chunks: Uint8Array<ArrayBuffer>[] = [];
82
82
  for await (const chunk of reader.iterateChunks()) {
83
83
  chunks.push(new Uint8Array(chunk));
84
84
  }
@@ -6,16 +6,16 @@
6
6
  * If you need to keep previous chunks, copy them to a new array.
7
7
  */
8
8
  export class ChunkStreamReader {
9
- private reader: ReadableStreamDefaultReader<Uint8Array>;
9
+ private reader: ReadableStreamDefaultReader<Uint8Array<ArrayBuffer>>;
10
10
 
11
11
  private chunkSize: number;
12
12
 
13
- constructor(stream: ReadableStream<Uint8Array>, chunkSize: number) {
13
+ constructor(stream: ReadableStream<Uint8Array<ArrayBuffer>>, chunkSize: number) {
14
14
  this.reader = stream.getReader();
15
15
  this.chunkSize = chunkSize;
16
16
  }
17
17
 
18
- async *iterateChunks(): AsyncGenerator<Uint8Array> {
18
+ async *iterateChunks(): AsyncGenerator<Uint8Array<ArrayBuffer>> {
19
19
  const buffer = new Uint8Array(this.chunkSize);
20
20
 
21
21
  let position = 0;
@@ -23,7 +23,7 @@ export class UploadCryptoService {
23
23
 
24
24
  async generateFileCrypto(
25
25
  parentUid: string,
26
- parentKeys: { key: PrivateKey; hashKey: Uint8Array },
26
+ parentKeys: { key: PrivateKey; hashKey: Uint8Array<ArrayBuffer> },
27
27
  name: string,
28
28
  ): Promise<NodeCrypto> {
29
29
  const signingKeys = await this.getSigningKeys({ parentNodeUid: parentUid });
@@ -118,14 +118,14 @@ export class UploadCryptoService {
118
118
  encryptedData: encryptedData,
119
119
  originalSize: thumbnail.thumbnail.length,
120
120
  encryptedSize: encryptedData.length,
121
- hash: new Uint8Array(digest),
121
+ hash: new Uint8Array<ArrayBuffer>(digest),
122
122
  };
123
123
  }
124
124
 
125
125
  async encryptBlock(
126
- verifyBlock: (encryptedBlock: Uint8Array) => Promise<{ verificationToken: Uint8Array }>,
126
+ verifyBlock: (encryptedBlock: Uint8Array<ArrayBuffer>) => Promise<{ verificationToken: Uint8Array<ArrayBuffer> }>,
127
127
  nodeRevisionDraftKeys: NodeRevisionDraftKeys,
128
- block: Uint8Array,
128
+ block: Uint8Array<ArrayBuffer>,
129
129
  index: number,
130
130
  ): Promise<EncryptedBlock> {
131
131
  const { encryptedData, armoredSignature } = await this.driveCrypto.encryptBlock(
@@ -145,13 +145,13 @@ export class UploadCryptoService {
145
145
  verificationToken,
146
146
  originalSize: block.length,
147
147
  encryptedSize: encryptedData.length,
148
- hash: new Uint8Array(digest),
148
+ hash: new Uint8Array<ArrayBuffer>(digest),
149
149
  };
150
150
  }
151
151
 
152
152
  async commitFile(
153
153
  nodeRevisionDraftKeys: NodeRevisionDraftKeys,
154
- manifest: Uint8Array,
154
+ manifest: Uint8Array<ArrayBuffer>,
155
155
  extendedAttributes?: string,
156
156
  ): Promise<{
157
157
  armoredManifestSignature: string;
@@ -190,10 +190,10 @@ export class UploadCryptoService {
190
190
 
191
191
  async verifyBlock(
192
192
  contentKeyPacketSessionKey: SessionKey,
193
- verificationCode: Uint8Array,
194
- encryptedData: Uint8Array,
193
+ verificationCode: Uint8Array<ArrayBuffer>,
194
+ encryptedData: Uint8Array<ArrayBuffer>,
195
195
  ): Promise<{
196
- verificationToken: Uint8Array;
196
+ verificationToken: Uint8Array<ArrayBuffer>;
197
197
  }> {
198
198
  // Attempt to decrypt data block, to try to detect bitflips / bad hardware
199
199
  //
@@ -8,7 +8,7 @@ export type NodeRevisionDraft = {
8
8
  nodeRevisionUid: string;
9
9
  nodeKeys: NodeRevisionDraftKeys;
10
10
  parentNodeKeys?: {
11
- hashKey: Uint8Array;
11
+ hashKey: Uint8Array<ArrayBuffer>;
12
12
  };
13
13
  // newNodeInfo is set only when revision is created with the new node.
14
14
  newNodeInfo?: {
@@ -64,19 +64,19 @@ export type NodeCryptoSigningKeys = {
64
64
  export type EncryptedBlockMetadata = {
65
65
  encryptedSize: number;
66
66
  originalSize: number;
67
- hash: Uint8Array;
67
+ hash: Uint8Array<ArrayBuffer>;
68
68
  };
69
69
 
70
70
  export type EncryptedBlock = EncryptedBlockMetadata & {
71
71
  index: number;
72
- encryptedData: Uint8Array;
72
+ encryptedData: Uint8Array<ArrayBuffer>;
73
73
  armoredSignature: string;
74
- verificationToken: Uint8Array;
74
+ verificationToken: Uint8Array<ArrayBuffer>;
75
75
  };
76
76
 
77
77
  export type EncryptedThumbnail = EncryptedBlockMetadata & {
78
78
  type: ThumbnailType;
79
- encryptedData: Uint8Array;
79
+ encryptedData: Uint8Array<ArrayBuffer>;
80
80
  };
81
81
 
82
82
  export type UploadTokens = {
@@ -101,7 +101,7 @@ export interface NodesService {
101
101
  key: PrivateKey;
102
102
  passphraseSessionKey: SessionKey;
103
103
  contentKeyPacketSessionKey?: SessionKey;
104
- hashKey?: Uint8Array;
104
+ hashKey?: Uint8Array<ArrayBuffer>;
105
105
  }>;
106
106
  getNodeSigningKeys(
107
107
  uids: { nodeUid: string; parentNodeUid?: string } | { nodeUid?: string; parentNodeUid: string },
@@ -73,7 +73,7 @@ export class UploadManager {
73
73
 
74
74
  private async createDraftOnAPI(
75
75
  parentFolderUid: string,
76
- parentHashKey: Uint8Array,
76
+ parentHashKey: Uint8Array<ArrayBuffer>,
77
77
  name: string,
78
78
  metadata: UploadMetadata,
79
79
  generatedNodeCrypto: NodeCrypto,
@@ -226,7 +226,7 @@ export class UploadManager {
226
226
 
227
227
  async commitDraft(
228
228
  nodeRevisionDraft: NodeRevisionDraft,
229
- manifest: Uint8Array,
229
+ manifest: Uint8Array<ArrayBuffer>,
230
230
  extendedAttributes: {
231
231
  modificationTime?: Date;
232
232
  size: number;
@@ -655,7 +655,7 @@ export class StreamUploader {
655
655
  return uploadedBlocks.map((block) => block.originalSize);
656
656
  }
657
657
 
658
- protected get manifest(): Uint8Array {
658
+ protected get manifest(): Uint8Array<ArrayBuffer> {
659
659
  this.uploadedThumbnails.sort((a, b) => a.type - b.type);
660
660
  this.uploadedBlocks.sort((a, b) => a.index - b.index);
661
661
  const hashes = [
@@ -223,12 +223,12 @@ export class ProtonDriveClient {
223
223
  return keys.contentKeyPacketSessionKey;
224
224
  },
225
225
  getPublicLinkInfo: async (url: string) => {
226
- const { token } = getTokenAndPasswordFromUrl(url)
226
+ const { token } = getTokenAndPasswordFromUrl(url);
227
227
  this.logger.info(`Getting info for public link token ${token}`);
228
228
  return this.publicSessionManager.getInfo(token);
229
229
  },
230
230
  authPublicLink: async (url: string, customPassword?: string, isAnonymousContext: boolean = false) => {
231
- const { token, password: urlPassword } = getTokenAndPasswordFromUrl(url)
231
+ const { token, password: urlPassword } = getTokenAndPasswordFromUrl(url);
232
232
  this.logger.info(`Authenticating public link token ${token}`);
233
233
 
234
234
  const { httpClient, shareKey, rootUid, publicRole } = await this.publicSessionManager.auth(
@@ -681,7 +681,7 @@ export class ProtonDriveClient {
681
681
  * @param customPassword - The optional custom password.
682
682
  */
683
683
  async createBookmark(url: string, customPassword?: string): Promise<void> {
684
- const { token, password: urlPassword } = getTokenAndPasswordFromUrl(url)
684
+ const { token, password: urlPassword } = getTokenAndPasswordFromUrl(url);
685
685
  this.logger.info(`Creating bookmark for token ${token}`);
686
686
  await this.sharing.access.createBookmark(token, urlPassword, customPassword);
687
687
  }
@@ -912,6 +912,18 @@ export class ProtonDriveClient {
912
912
  yield* this.devices.iterateDevices(signal);
913
913
  }
914
914
 
915
+ /**
916
+ * Get the device entity by its UID.
917
+ *
918
+ * @param deviceOrUid - Device entity or its UID string.
919
+ * @returns The device entity.
920
+ * @throws {@link ValidationError} If the device is not found.
921
+ */
922
+ async getDevice(deviceOrUid: DeviceOrUid): Promise<Device> {
923
+ this.logger.info(`Getting device ${getUid(deviceOrUid)}`);
924
+ return this.devices.getDevice(getUid(deviceOrUid));
925
+ }
926
+
915
927
  /**
916
928
  * Creates a new device.
917
929
  *
@@ -17,6 +17,7 @@ import {
17
17
  NonProtonInvitationOrUid,
18
18
  ProtonInvitationWithNode,
19
19
  NodeResult,
20
+ NodeResultWithError,
20
21
  } from './interface';
21
22
  import { getConfig } from './config';
22
23
  import { DriveCrypto } from './crypto';
@@ -38,6 +39,9 @@ import {
38
39
  initPhotoSharesModule,
39
40
  initPhotoUploadModule,
40
41
  initPhotosNodesModule,
42
+ AlbumItem,
43
+ TimelineItem,
44
+ PhotoTag,
41
45
  } from './internal/photos';
42
46
  import { SDKEvents } from './internal/sdkEvents';
43
47
  import { initSharesModule } from './internal/shares';
@@ -117,13 +121,7 @@ export class ProtonDrivePhotosClient {
117
121
  this.photoShares,
118
122
  fullConfig.clientUid,
119
123
  );
120
- this.photos = initPhotosModule(
121
- telemetry,
122
- apiService,
123
- cryptoModule,
124
- this.photoShares,
125
- this.nodes.access,
126
- );
124
+ this.photos = initPhotosModule(telemetry, apiService, cryptoModule, this.photoShares, this.nodes.access);
127
125
  this.sharing = initSharingModule(
128
126
  telemetry,
129
127
  apiService,
@@ -224,12 +222,7 @@ export class ProtonDrivePhotosClient {
224
222
  * The output is sorted by the capture time, starting from the
225
223
  * the most recent photos.
226
224
  */
227
- async *iterateTimeline(signal?: AbortSignal): AsyncGenerator<{
228
- nodeUid: string;
229
- captureTime: Date;
230
- tags: number[];
231
- }> {
232
- // TODO: expose better type
225
+ async *iterateTimeline(signal?: AbortSignal): AsyncGenerator<TimelineItem> {
233
226
  yield* this.photos.timeline.iterateTimeline(signal);
234
227
  }
235
228
 
@@ -240,7 +233,7 @@ export class ProtonDrivePhotosClient {
240
233
  */
241
234
  async *iterateTrashedNodes(signal?: AbortSignal): AsyncGenerator<MaybePhotoNode> {
242
235
  this.logger.info('Iterating trashed nodes');
243
- yield * convertInternalPhotoNodeIterator(this.nodes.access.iterateTrashedNodes(signal));
236
+ yield* convertInternalPhotoNodeIterator(this.nodes.access.iterateTrashedNodes(signal));
244
237
  }
245
238
 
246
239
  /**
@@ -251,7 +244,7 @@ export class ProtonDrivePhotosClient {
251
244
  async *iterateNodes(nodeUids: NodeOrUid[], signal?: AbortSignal): AsyncGenerator<MaybeMissingPhotoNode> {
252
245
  this.logger.info(`Iterating ${nodeUids.length} nodes`);
253
246
  // TODO: expose photo type
254
- yield * convertInternalMissingPhotoNodeIterator(this.nodes.access.iterateNodes(getUids(nodeUids), signal));
247
+ yield* convertInternalMissingPhotoNodeIterator(this.nodes.access.iterateNodes(getUids(nodeUids), signal));
255
248
  }
256
249
 
257
250
  /**
@@ -301,7 +294,7 @@ export class ProtonDrivePhotosClient {
301
294
  */
302
295
  async *deleteNodes(nodeUids: NodeOrUid[], signal?: AbortSignal): AsyncGenerator<NodeResult> {
303
296
  this.logger.info(`Deleting ${nodeUids.length} nodes`);
304
- yield * this.nodes.management.deleteTrashedNodes(getUids(nodeUids), signal);
297
+ yield* this.nodes.management.deleteTrashedNodes(getUids(nodeUids), signal);
305
298
  }
306
299
 
307
300
  /**
@@ -319,7 +312,7 @@ export class ProtonDrivePhotosClient {
319
312
  */
320
313
  async *iterateSharedNodes(signal?: AbortSignal): AsyncGenerator<MaybePhotoNode> {
321
314
  this.logger.info('Iterating shared nodes by me');
322
- yield * convertInternalPhotoNodeIterator(this.sharing.access.iterateSharedNodes(signal));
315
+ yield* convertInternalPhotoNodeIterator(this.sharing.access.iterateSharedNodes(signal));
323
316
  }
324
317
 
325
318
  /**
@@ -456,8 +449,7 @@ export class ProtonDrivePhotosClient {
456
449
  metadata: UploadMetadata & {
457
450
  captureTime?: Date;
458
451
  mainPhotoLinkID?: string;
459
- // TODO: handle tags enum in the SDK
460
- tags?: (0 | 3 | 1 | 2 | 7 | 4 | 5 | 6 | 8 | 9)[];
452
+ tags?: PhotoTag[];
461
453
  },
462
454
  signal?: AbortSignal,
463
455
  ): Promise<FileUploader> {
@@ -487,7 +479,9 @@ export class ProtonDrivePhotosClient {
487
479
  */
488
480
  async isDuplicatePhoto(name: string, generateSha1: () => Promise<string>, signal?: AbortSignal): Promise<boolean> {
489
481
  this.logger.info(`Checking if photo is a duplicate`);
490
- return this.photos.timeline.findPhotoDuplicates(name, generateSha1, signal).then(nodeUids => nodeUids.length !== 0);
482
+ return this.photos.timeline
483
+ .findPhotoDuplicates(name, generateSha1, signal)
484
+ .then((nodeUids) => nodeUids.length !== 0);
491
485
  }
492
486
 
493
487
  /**
@@ -508,7 +502,11 @@ export class ProtonDrivePhotosClient {
508
502
  * @param signal - An optional abort signal to cancel the operation.
509
503
  * @returns An array of node UIDs of duplicate photos. Empty array if no duplicates found.
510
504
  */
511
- async findPhotoDuplicates(name: string, generateSha1: () => Promise<string>, signal?: AbortSignal): Promise<string[]> {
505
+ async findPhotoDuplicates(
506
+ name: string,
507
+ generateSha1: () => Promise<string>,
508
+ signal?: AbortSignal,
509
+ ): Promise<string[]> {
512
510
  this.logger.info(`Checking if photo have duplicates`);
513
511
  return this.photos.timeline.findPhotoDuplicates(name, generateSha1, signal);
514
512
  }
@@ -578,6 +576,64 @@ export class ProtonDrivePhotosClient {
578
576
  async *iterateAlbums(signal?: AbortSignal): AsyncGenerator<MaybePhotoNode> {
579
577
  this.logger.info('Iterating albums');
580
578
  // TODO: expose album type
581
- yield * convertInternalPhotoNodeIterator(this.photos.albums.iterateAlbums(signal));
579
+ yield* convertInternalPhotoNodeIterator(this.photos.albums.iterateAlbums(signal));
580
+ }
581
+
582
+ /**
583
+ * Iterates the photo placeholders of the given album.
584
+ *
585
+ * The output is sorted by the capture time, starting from the
586
+ * the most recent photos.
587
+ *
588
+ * @param albumNodeUid - The UID of the album.
589
+ * @param signal - An optional abort the operation.
590
+ */
591
+ async *iterateAlbum(albumNodeUid: NodeOrUid, signal?: AbortSignal): AsyncGenerator<AlbumItem> {
592
+ this.logger.info(`Iterating photos of album ${getUid(albumNodeUid)}`);
593
+ yield* this.photos.albums.iterateAlbum(getUid(albumNodeUid), signal);
594
+ }
595
+
596
+ /**
597
+ * Adds photos to an album.
598
+ *
599
+ * Photos are added in batches. Each photo's related photos (e.g., live
600
+ * photo components) are always included with the main photo.
601
+ *
602
+ * The album has a limit of 10,000 photos. If the limit is reached,
603
+ * a `ValidationError` is thrown.
604
+ *
605
+ * @param albumNodeUid - The UID of the album to add photos to.
606
+ * @param photoNodeUids - The UIDs of the photos to add to the album.
607
+ * @param signal - An optional abort signal to cancel the operation.
608
+ * @returns An async generator of the added photo results.
609
+ */
610
+ async *addPhotosToAlbum(
611
+ albumNodeUid: NodeOrUid,
612
+ photoNodeUids: NodeOrUid[],
613
+ signal?: AbortSignal,
614
+ ): AsyncGenerator<NodeResultWithError> {
615
+ this.logger.info(`Adding ${photoNodeUids.length} photos to album ${getUid(albumNodeUid)}`);
616
+ yield* this.photos.albums.addPhotos(getUid(albumNodeUid), getUids(photoNodeUids), signal);
617
+ }
618
+
619
+ /**
620
+ * Removes photos from an album.
621
+ *
622
+ * Photos are not deleted, they are just removed from the album.
623
+ * If a photo was added to the timeline by the user, it will remain
624
+ * in the timeline after being removed from the album.
625
+ *
626
+ * @param albumNodeUid - The UID of the album to remove photos from.
627
+ * @param photoNodeUids - The UIDs of the photos to remove from the album.
628
+ * @param signal - An optional abort signal to cancel the operation.
629
+ * @returns An async generator of the removed photo results.
630
+ */
631
+ async *removePhotosFromAlbum(
632
+ albumNodeUid: NodeOrUid,
633
+ photoNodeUids: NodeOrUid[],
634
+ signal?: AbortSignal,
635
+ ): AsyncGenerator<NodeResultWithError> {
636
+ this.logger.info(`Removing ${photoNodeUids.length} photos from album ${getUid(albumNodeUid)}`);
637
+ yield* this.photos.albums.removePhotos(getUid(albumNodeUid), getUids(photoNodeUids), signal);
582
638
  }
583
639
  }
@@ -350,4 +350,17 @@ export class ProtonDrivePublicLinkClient {
350
350
  this.logger.info(`Getting file revision uploader for ${getUid(nodeUid)}`);
351
351
  return this.upload.getFileRevisionUploader(getUid(nodeUid), metadata, signal);
352
352
  }
353
+
354
+ /**
355
+ * Returns the available name for the file in the given parent folder.
356
+ *
357
+ * The function will return a name that includes the original name with the
358
+ * available index. The name is guaranteed to be unique in the parent folder.
359
+ *
360
+ * Example new name: `file (2).txt`.
361
+ */
362
+ async getAvailableName(parentFolderUid: NodeOrUid, name: string): Promise<string> {
363
+ this.logger.info(`Getting available name in folder ${getUid(parentFolderUid)}`);
364
+ return this.sharingPublic.nodes.management.findAvailableName(getUid(parentFolderUid), name);
365
+ }
353
366
  }
@@ -160,11 +160,13 @@ export function convertInternalPhotoNode(photoNode: InternalPartialPhotoNode): P
160
160
  return resultOk({
161
161
  ...node.value,
162
162
  photo: photoNode.photo,
163
+ album: photoNode.album,
163
164
  } as PublicPhotoNode);
164
165
  }
165
166
  return resultError({
166
167
  ...node.error,
167
168
  photo: photoNode.photo,
169
+ album: photoNode.album,
168
170
  } as PublicDegradedPhotoNode);
169
171
  }
170
172