@protontech/drive-sdk 0.6.2 → 0.7.1

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 (158) hide show
  1. package/dist/interface/index.d.ts +1 -0
  2. package/dist/interface/index.js.map +1 -1
  3. package/dist/interface/nodes.d.ts +14 -10
  4. package/dist/interface/nodes.js +5 -8
  5. package/dist/interface/nodes.js.map +1 -1
  6. package/dist/interface/photos.d.ts +62 -0
  7. package/dist/interface/photos.js +3 -0
  8. package/dist/interface/photos.js.map +1 -0
  9. package/dist/internal/apiService/apiService.d.ts +2 -2
  10. package/dist/internal/apiService/apiService.js.map +1 -1
  11. package/dist/internal/apiService/driveTypes.d.ts +1294 -517
  12. package/dist/internal/apiService/errors.js +4 -3
  13. package/dist/internal/apiService/errors.js.map +1 -1
  14. package/dist/internal/download/cryptoService.js +8 -6
  15. package/dist/internal/download/cryptoService.js.map +1 -1
  16. package/dist/internal/download/fileDownloader.d.ts +2 -1
  17. package/dist/internal/download/fileDownloader.js +6 -3
  18. package/dist/internal/download/fileDownloader.js.map +1 -1
  19. package/dist/internal/download/index.d.ts +1 -1
  20. package/dist/internal/download/index.js +3 -3
  21. package/dist/internal/download/index.js.map +1 -1
  22. package/dist/internal/errors.d.ts +1 -0
  23. package/dist/internal/errors.js +4 -0
  24. package/dist/internal/errors.js.map +1 -1
  25. package/dist/internal/nodes/apiService.d.ts +68 -16
  26. package/dist/internal/nodes/apiService.js +138 -85
  27. package/dist/internal/nodes/apiService.js.map +1 -1
  28. package/dist/internal/nodes/apiService.test.js +7 -5
  29. package/dist/internal/nodes/apiService.test.js.map +1 -1
  30. package/dist/internal/nodes/cache.d.ts +16 -8
  31. package/dist/internal/nodes/cache.js +19 -5
  32. package/dist/internal/nodes/cache.js.map +1 -1
  33. package/dist/internal/nodes/cache.test.js +1 -0
  34. package/dist/internal/nodes/cache.test.js.map +1 -1
  35. package/dist/internal/nodes/cryptoReporter.d.ts +3 -3
  36. package/dist/internal/nodes/cryptoReporter.js.map +1 -1
  37. package/dist/internal/nodes/cryptoService.d.ts +13 -22
  38. package/dist/internal/nodes/cryptoService.js +47 -16
  39. package/dist/internal/nodes/cryptoService.js.map +1 -1
  40. package/dist/internal/nodes/cryptoService.test.js +262 -17
  41. package/dist/internal/nodes/cryptoService.test.js.map +1 -1
  42. package/dist/internal/nodes/events.d.ts +2 -2
  43. package/dist/internal/nodes/events.js.map +1 -1
  44. package/dist/internal/nodes/index.test.js +1 -0
  45. package/dist/internal/nodes/index.test.js.map +1 -1
  46. package/dist/internal/nodes/interface.d.ts +14 -3
  47. package/dist/internal/nodes/nodesAccess.d.ts +36 -20
  48. package/dist/internal/nodes/nodesAccess.js +54 -29
  49. package/dist/internal/nodes/nodesAccess.js.map +1 -1
  50. package/dist/internal/nodes/nodesManagement.d.ts +34 -14
  51. package/dist/internal/nodes/nodesManagement.js +44 -31
  52. package/dist/internal/nodes/nodesManagement.js.map +1 -1
  53. package/dist/internal/nodes/nodesManagement.test.js +60 -14
  54. package/dist/internal/nodes/nodesManagement.test.js.map +1 -1
  55. package/dist/internal/nodes/nodesRevisions.d.ts +2 -2
  56. package/dist/internal/nodes/nodesRevisions.js.map +1 -1
  57. package/dist/internal/photos/albums.d.ts +2 -2
  58. package/dist/internal/photos/albums.js.map +1 -1
  59. package/dist/internal/photos/index.d.ts +19 -3
  60. package/dist/internal/photos/index.js +38 -8
  61. package/dist/internal/photos/index.js.map +1 -1
  62. package/dist/internal/photos/interface.d.ts +18 -9
  63. package/dist/internal/photos/nodes.d.ts +57 -0
  64. package/dist/internal/photos/nodes.js +165 -0
  65. package/dist/internal/photos/nodes.js.map +1 -0
  66. package/dist/internal/photos/timeline.d.ts +2 -2
  67. package/dist/internal/photos/timeline.js.map +1 -1
  68. package/dist/internal/photos/timeline.test.js.map +1 -1
  69. package/dist/internal/photos/upload.d.ts +2 -2
  70. package/dist/internal/photos/upload.js.map +1 -1
  71. package/dist/internal/sharingPublic/index.d.ts +6 -6
  72. package/dist/internal/sharingPublic/index.js +8 -7
  73. package/dist/internal/sharingPublic/index.js.map +1 -1
  74. package/dist/internal/sharingPublic/nodes.d.ts +16 -3
  75. package/dist/internal/sharingPublic/nodes.js +34 -2
  76. package/dist/internal/sharingPublic/nodes.js.map +1 -1
  77. package/dist/internal/sharingPublic/unauthApiService.d.ts +17 -0
  78. package/dist/internal/sharingPublic/unauthApiService.js +31 -0
  79. package/dist/internal/sharingPublic/unauthApiService.js.map +1 -0
  80. package/dist/internal/sharingPublic/unauthApiService.test.d.ts +1 -0
  81. package/dist/internal/sharingPublic/unauthApiService.test.js +27 -0
  82. package/dist/internal/sharingPublic/unauthApiService.test.js.map +1 -0
  83. package/dist/internal/upload/apiService.d.ts +4 -3
  84. package/dist/internal/upload/apiService.js.map +1 -1
  85. package/dist/internal/upload/cryptoService.d.ts +8 -3
  86. package/dist/internal/upload/cryptoService.js +45 -9
  87. package/dist/internal/upload/cryptoService.js.map +1 -1
  88. package/dist/internal/upload/fileUploader.test.js +1 -1
  89. package/dist/internal/upload/fileUploader.test.js.map +1 -1
  90. package/dist/internal/upload/interface.d.ts +25 -13
  91. package/dist/internal/upload/manager.js +7 -4
  92. package/dist/internal/upload/manager.js.map +1 -1
  93. package/dist/internal/upload/manager.test.js +5 -4
  94. package/dist/internal/upload/manager.test.js.map +1 -1
  95. package/dist/internal/upload/streamUploader.js +9 -4
  96. package/dist/internal/upload/streamUploader.js.map +1 -1
  97. package/dist/internal/upload/streamUploader.test.js +8 -5
  98. package/dist/internal/upload/streamUploader.test.js.map +1 -1
  99. package/dist/protonDriveClient.d.ts +11 -2
  100. package/dist/protonDriveClient.js +20 -4
  101. package/dist/protonDriveClient.js.map +1 -1
  102. package/dist/protonDrivePhotosClient.d.ts +8 -8
  103. package/dist/protonDrivePhotosClient.js +8 -9
  104. package/dist/protonDrivePhotosClient.js.map +1 -1
  105. package/dist/protonDrivePublicLinkClient.d.ts +9 -2
  106. package/dist/protonDrivePublicLinkClient.js +16 -5
  107. package/dist/protonDrivePublicLinkClient.js.map +1 -1
  108. package/dist/transformers.d.ts +7 -2
  109. package/dist/transformers.js +37 -0
  110. package/dist/transformers.js.map +1 -1
  111. package/package.json +1 -1
  112. package/src/interface/index.ts +1 -0
  113. package/src/interface/nodes.ts +14 -11
  114. package/src/interface/photos.ts +67 -0
  115. package/src/internal/apiService/apiService.ts +2 -2
  116. package/src/internal/apiService/driveTypes.ts +1294 -517
  117. package/src/internal/apiService/errors.ts +5 -4
  118. package/src/internal/download/cryptoService.ts +13 -6
  119. package/src/internal/download/fileDownloader.ts +4 -2
  120. package/src/internal/download/index.ts +3 -0
  121. package/src/internal/errors.ts +4 -0
  122. package/src/internal/nodes/apiService.test.ts +7 -5
  123. package/src/internal/nodes/apiService.ts +210 -124
  124. package/src/internal/nodes/cache.test.ts +1 -0
  125. package/src/internal/nodes/cache.ts +32 -13
  126. package/src/internal/nodes/cryptoReporter.ts +3 -3
  127. package/src/internal/nodes/cryptoService.test.ts +380 -18
  128. package/src/internal/nodes/cryptoService.ts +77 -36
  129. package/src/internal/nodes/events.ts +2 -2
  130. package/src/internal/nodes/index.test.ts +1 -0
  131. package/src/internal/nodes/interface.ts +17 -2
  132. package/src/internal/nodes/nodesAccess.ts +99 -54
  133. package/src/internal/nodes/nodesManagement.test.ts +69 -14
  134. package/src/internal/nodes/nodesManagement.ts +94 -48
  135. package/src/internal/nodes/nodesRevisions.ts +3 -3
  136. package/src/internal/photos/albums.ts +2 -2
  137. package/src/internal/photos/index.ts +45 -3
  138. package/src/internal/photos/interface.ts +21 -9
  139. package/src/internal/photos/nodes.ts +233 -0
  140. package/src/internal/photos/timeline.test.ts +2 -2
  141. package/src/internal/photos/timeline.ts +2 -2
  142. package/src/internal/photos/upload.ts +3 -3
  143. package/src/internal/sharingPublic/index.ts +7 -3
  144. package/src/internal/sharingPublic/nodes.ts +43 -2
  145. package/src/internal/sharingPublic/unauthApiService.test.ts +29 -0
  146. package/src/internal/sharingPublic/unauthApiService.ts +32 -0
  147. package/src/internal/upload/apiService.ts +4 -3
  148. package/src/internal/upload/cryptoService.ts +73 -12
  149. package/src/internal/upload/fileUploader.test.ts +1 -1
  150. package/src/internal/upload/interface.ts +24 -13
  151. package/src/internal/upload/manager.test.ts +5 -4
  152. package/src/internal/upload/manager.ts +7 -4
  153. package/src/internal/upload/streamUploader.test.ts +8 -5
  154. package/src/internal/upload/streamUploader.ts +10 -4
  155. package/src/protonDriveClient.ts +27 -5
  156. package/src/protonDrivePhotosClient.ts +23 -23
  157. package/src/protonDrivePublicLinkClient.ts +19 -3
  158. package/src/transformers.ts +49 -2
@@ -1,6 +1,6 @@
1
1
  import { PrivateKey, SessionKey } from '../../crypto';
2
2
 
3
- import { MetricVolumeType, ThumbnailType, Result, Revision } from '../../interface';
3
+ import { MetricVolumeType, ThumbnailType, Result, Revision, AnonymousUser } from '../../interface';
4
4
  import { DecryptedNode } from '../nodes';
5
5
 
6
6
  export type NodeRevisionDraft = {
@@ -22,7 +22,7 @@ export type NodeRevisionDraft = {
22
22
  export type NodeRevisionDraftKeys = {
23
23
  key: PrivateKey;
24
24
  contentKeyPacketSessionKey: SessionKey;
25
- signatureAddress: NodeCryptoSignatureAddress;
25
+ signingKeys: NodeCryptoSigningKeys;
26
26
  };
27
27
 
28
28
  export type NodeCrypto = {
@@ -51,13 +51,14 @@ export type NodeCrypto = {
51
51
  encryptedName: string;
52
52
  hash: string;
53
53
  };
54
- signatureAddress: NodeCryptoSignatureAddress;
54
+ signingKeys: NodeCryptoSigningKeys;
55
55
  };
56
56
 
57
- export type NodeCryptoSignatureAddress = {
58
- email: string;
59
- addressId: string;
60
- addressKey: PrivateKey;
57
+ export type NodeCryptoSigningKeys = {
58
+ email: string | AnonymousUser;
59
+ addressId: string | AnonymousUser;
60
+ nameAndPassphraseSigningKey: PrivateKey;
61
+ contentSigningKey: PrivateKey;
61
62
  };
62
63
 
63
64
  export type EncryptedBlockMetadata = {
@@ -102,12 +103,9 @@ export interface NodesService {
102
103
  contentKeyPacketSessionKey?: SessionKey;
103
104
  hashKey?: Uint8Array;
104
105
  }>;
105
- getRootNodeEmailKey(nodeUid: string): Promise<{
106
- email: string;
107
- addressId: string;
108
- addressKey: PrivateKey;
109
- addressKeyId: string;
110
- }>;
106
+ getNodeSigningKeys(
107
+ uids: { nodeUid: string; parentNodeUid?: string } | { nodeUid?: string; parentNodeUid: string },
108
+ ): Promise<NodeSigningKeys>;
111
109
  notifyChildCreated(nodeUid: string): Promise<void>;
112
110
  notifyNodeChanged(nodeUid: string): Promise<void>;
113
111
  }
@@ -126,6 +124,19 @@ export interface NodesServiceNode {
126
124
  activeRevision?: Result<Revision, Error>;
127
125
  }
128
126
 
127
+ export type NodeSigningKeys =
128
+ | {
129
+ type: 'userAddress';
130
+ email: string;
131
+ addressId: string;
132
+ key: PrivateKey;
133
+ }
134
+ | {
135
+ type: 'nodeKey';
136
+ nodeKey?: PrivateKey;
137
+ parentNodeKey?: PrivateKey;
138
+ };
139
+
129
140
  /**
130
141
  * Interface describing the dependencies to the shares module.
131
142
  */
@@ -50,7 +50,7 @@ describe('UploadManager', () => {
50
50
  encryptedName: 'newNode:encryptedName',
51
51
  hash: 'newNode:hash',
52
52
  },
53
- signatureAddress: {
53
+ signingKeys: {
54
54
  email: 'signatureEmail',
55
55
  },
56
56
  }),
@@ -69,7 +69,8 @@ describe('UploadManager', () => {
69
69
  hashKey: 'parentNode:hashKey',
70
70
  key: 'parentNode:nodekey',
71
71
  }),
72
- getRootNodeEmailKey: jest.fn().mockResolvedValue({
72
+ getNodeSigningKeys: jest.fn().mockResolvedValue({
73
+ type: 'userAddress',
73
74
  email: 'signatureEmail',
74
75
  addressId: 'addressId',
75
76
  }),
@@ -100,7 +101,7 @@ describe('UploadManager', () => {
100
101
  nodeKeys: {
101
102
  key: 'newNode:key',
102
103
  contentKeyPacketSessionKey: 'newNode:ContentKeyPacketSessionKey',
103
- signatureAddress: {
104
+ signingKeys: {
104
105
  email: 'signatureEmail',
105
106
  },
106
107
  },
@@ -153,7 +154,7 @@ describe('UploadManager', () => {
153
154
  nodeKeys: {
154
155
  key: 'newNode:key',
155
156
  contentKeyPacketSessionKey: 'newNode:ContentKeyPacketSessionKey',
156
- signatureAddress: {
157
+ signingKeys: {
157
158
  email: 'signatureEmail',
158
159
  },
159
160
  },
@@ -57,7 +57,7 @@ export class UploadManager {
57
57
  nodeKeys: {
58
58
  key: generatedNodeCrypto.nodeKeys.decrypted.key,
59
59
  contentKeyPacketSessionKey: generatedNodeCrypto.contentKey.decrypted.contentKeyPacketSessionKey,
60
- signatureAddress: generatedNodeCrypto.signatureAddress,
60
+ signingKeys: generatedNodeCrypto.signingKeys,
61
61
  },
62
62
  parentNodeKeys: {
63
63
  hashKey: parentKeys.hashKey,
@@ -93,7 +93,7 @@ export class UploadManager {
93
93
  base64ContentKeyPacket: generatedNodeCrypto.contentKey.encrypted.base64ContentKeyPacket,
94
94
  armoredContentKeyPacketSignature:
95
95
  generatedNodeCrypto.contentKey.encrypted.armoredContentKeyPacketSignature,
96
- signatureEmail: generatedNodeCrypto.signatureAddress.email,
96
+ signatureEmail: generatedNodeCrypto.signingKeys.email,
97
97
  });
98
98
  return result;
99
99
  } catch (error: unknown) {
@@ -192,7 +192,10 @@ export class UploadManager {
192
192
  throw new ValidationError(c('Error').t`Creating revisions in non-files is not allowed`);
193
193
  }
194
194
 
195
- const signatureAddress = await this.nodesService.getRootNodeEmailKey(nodeUid);
195
+ const signingKeys = await this.cryptoService.getSigningKeysForExistingNode({
196
+ nodeUid,
197
+ parentNodeUid: node.parentUid,
198
+ });
196
199
 
197
200
  const { nodeRevisionUid } = await this.apiService.createDraftRevision(nodeUid, {
198
201
  currentRevisionUid: node.activeRevision.value.uid,
@@ -205,7 +208,7 @@ export class UploadManager {
205
208
  nodeKeys: {
206
209
  key: nodeKeys.key,
207
210
  contentKeyPacketSessionKey: nodeKeys.contentKeyPacketSessionKey,
208
- signatureAddress: signatureAddress,
211
+ signingKeys,
209
212
  },
210
213
  };
211
214
  }
@@ -110,7 +110,9 @@ describe('StreamUploader', () => {
110
110
  nodeRevisionUid: 'revisionUid',
111
111
  nodeUid: 'nodeUid',
112
112
  nodeKeys: {
113
- signatureAddress: { addressId: 'addressId' },
113
+ signingKeys: {
114
+ addressId: 'addressId',
115
+ },
114
116
  },
115
117
  } as NodeRevisionDraft;
116
118
 
@@ -312,8 +314,9 @@ describe('StreamUploader', () => {
312
314
  throw new Error('Failed to encrypt block');
313
315
  });
314
316
 
315
- // Encrypting thumbnails is before blocks, thus it can be uploaded before failure.
316
- await verifyFailure('Failed to encrypt block', 1024);
317
+ // Thumbnail are uploaded with the first content block. If the
318
+ // content block fails to encrypt, nothing is uploaded.
319
+ await verifyFailure('Failed to encrypt block', 0);
317
320
  // 1 block + 1 retry, others are skipped
318
321
  expect(cryptoService.encryptBlock).toHaveBeenCalledTimes(2);
319
322
  });
@@ -415,7 +418,7 @@ describe('StreamUploader', () => {
415
418
  expect(apiService.requestBlockUpload).toHaveBeenCalledTimes(2);
416
419
  expect(apiService.requestBlockUpload).toHaveBeenCalledWith(
417
420
  revisionDraft.nodeRevisionUid,
418
- revisionDraft.nodeKeys.signatureAddress.addressId,
421
+ revisionDraft.nodeKeys.signingKeys.addressId,
419
422
  {
420
423
  contentBlocks: [
421
424
  {
@@ -436,7 +439,7 @@ describe('StreamUploader', () => {
436
439
  describe('verifyIntegrity', () => {
437
440
  it('should report block verification error', async () => {
438
441
  blockVerifier.verifyBlock = jest.fn().mockRejectedValue(new IntegrityError('Block verification error'));
439
- await verifyFailure('Block verification error', 1024);
442
+ await verifyFailure('Block verification error', 0);
440
443
  expect(telemetry.logBlockVerificationError).toHaveBeenCalledWith(false);
441
444
  });
442
445
 
@@ -319,7 +319,7 @@ export class StreamUploader {
319
319
  this.logger.info(`Requesting upload tokens for ${this.encryptedBlocks.size} blocks`);
320
320
  const uploadTokens = await this.apiService.requestBlockUpload(
321
321
  this.revisionDraft.nodeRevisionUid,
322
- this.revisionDraft.nodeKeys.signatureAddress.addressId,
322
+ this.revisionDraft.nodeKeys.signingKeys.addressId,
323
323
  {
324
324
  contentBlocks: Array.from(
325
325
  this.encryptedBlocks.values().map((block) => ({
@@ -340,6 +340,12 @@ export class StreamUploader {
340
340
  },
341
341
  );
342
342
 
343
+ // If the upload was aborted while requesting next upload tokens,
344
+ // do not schedule any next upload.
345
+ if (this.isUploadAborted) {
346
+ throw this.error || new AbortError();
347
+ }
348
+
343
349
  for (const thumbnailToken of uploadTokens.thumbnailTokens) {
344
350
  let encryptedThumbnail = this.encryptedThumbnails.get(thumbnailToken.type);
345
351
  if (!encryptedThumbnail) {
@@ -418,7 +424,7 @@ export class StreamUploader {
418
424
  break;
419
425
  } catch (error: unknown) {
420
426
  // Do not retry or report anything if the upload was aborted.
421
- if (error instanceof AbortError) {
427
+ if (error instanceof AbortError || this.isUploadAborted) {
422
428
  throw error;
423
429
  }
424
430
 
@@ -485,7 +491,7 @@ export class StreamUploader {
485
491
  break;
486
492
  } catch (error: unknown) {
487
493
  // Do not retry or report anything if the upload was aborted.
488
- if (error instanceof AbortError) {
494
+ if (error instanceof AbortError || this.isUploadAborted) {
489
495
  throw error;
490
496
  }
491
497
 
@@ -501,7 +507,7 @@ export class StreamUploader {
501
507
  logger.warn(`Token expired, fetching new token and retrying`);
502
508
  const uploadTokens = await this.apiService.requestBlockUpload(
503
509
  this.revisionDraft.nodeRevisionUid,
504
- this.revisionDraft.nodeKeys.signatureAddress.addressId,
510
+ this.revisionDraft.nodeKeys.signingKeys.addressId,
505
511
  {
506
512
  contentBlocks: [
507
513
  {
@@ -106,7 +106,11 @@ export class ProtonDriveClient {
106
106
  * Experimental feature to authenticate a public link and
107
107
  * return the client for the public link to access it.
108
108
  */
109
- authPublicLink: (url: string, customPassword?: string) => Promise<ProtonDrivePublicLinkClient>;
109
+ authPublicLink: (
110
+ url: string,
111
+ customPassword?: string,
112
+ isAnonymousContext?: boolean,
113
+ ) => Promise<ProtonDrivePublicLinkClient>;
110
114
  };
111
115
 
112
116
  constructor({
@@ -222,7 +226,7 @@ export class ProtonDriveClient {
222
226
  this.logger.info(`Getting info for public link ${url}`);
223
227
  return this.publicSessionManager.getInfo(url);
224
228
  },
225
- authPublicLink: async (url: string, customPassword?: string) => {
229
+ authPublicLink: async (url: string, customPassword?: string, isAnonymousContext: boolean = false) => {
226
230
  this.logger.info(`Authenticating public link ${url}`);
227
231
  const { httpClient, token, shareKey, rootUid } = await this.publicSessionManager.auth(
228
232
  url,
@@ -239,6 +243,7 @@ export class ProtonDriveClient {
239
243
  token,
240
244
  publicShareKey: shareKey,
241
245
  publicRootNodeUid: rootUid,
246
+ isAnonymousContext,
242
247
  });
243
248
  },
244
249
  };
@@ -419,6 +424,12 @@ export class ProtonDriveClient {
419
424
  * The operation is performed node by node and the results are yielded
420
425
  * as they are available. Order of the results is not guaranteed.
421
426
  *
427
+ * The `nodeUids` can be a list of node entities or their UIDs, or a list
428
+ * of objects with `uid` and `name` properties where the name is the new
429
+ * name of the copied node. By default, the name is the same as the
430
+ * original node. Use `getAvailableName` to get the available name for the
431
+ * new node in the target parent node in case of a name conflict.
432
+ *
422
433
  * If one of the nodes fails to copy, the operation continues with the
423
434
  * rest of the nodes. Use `NodeResult` to check the status of the action.
424
435
  *
@@ -428,12 +439,23 @@ export class ProtonDriveClient {
428
439
  * @returns An async generator of the results of the copy operation
429
440
  */
430
441
  async *copyNodes(
431
- nodeUids: NodeOrUid[],
442
+ nodesOrNodeUidsOrWithNames: (NodeOrUid | { uid: string; name: string })[],
432
443
  newParentNodeUid: NodeOrUid,
433
444
  signal?: AbortSignal,
434
445
  ): AsyncGenerator<NodeResultWithNewUid> {
435
- this.logger.info(`Copying ${nodeUids.length} nodes to ${getUid(newParentNodeUid)}`);
436
- yield* this.nodes.management.copyNodes(getUids(nodeUids), getUid(newParentNodeUid), signal);
446
+ this.logger.info(`Copying ${nodesOrNodeUidsOrWithNames.length} nodes to ${getUid(newParentNodeUid)}`);
447
+
448
+ const nodeUidsOrWithNames = nodesOrNodeUidsOrWithNames.map((param) => {
449
+ if (typeof param === 'string') {
450
+ return param;
451
+ }
452
+ if ('uid' in param && 'name' in param && typeof param.uid === 'string' && typeof param.name === 'string') {
453
+ return { uid: param.uid, name: param.name };
454
+ }
455
+ return getUid(param);
456
+ });
457
+
458
+ yield* this.nodes.management.copyNodes(nodeUidsOrWithNames, getUid(newParentNodeUid), signal);
437
459
  }
438
460
 
439
461
  /**
@@ -2,12 +2,12 @@ import {
2
2
  Logger,
3
3
  ProtonDriveClientContructorParameters,
4
4
  NodeOrUid,
5
- MaybeMissingNode,
5
+ MaybeMissingPhotoNode,
6
6
  UploadMetadata,
7
7
  FileDownloader,
8
8
  FileUploader,
9
9
  SDKEvent,
10
- MaybeNode,
10
+ MaybePhotoNode,
11
11
  ThumbnailType,
12
12
  ThumbnailResult,
13
13
  ShareNodeSettings,
@@ -22,22 +22,22 @@ import { getConfig } from './config';
22
22
  import { DriveCrypto } from './crypto';
23
23
  import { Telemetry } from './telemetry';
24
24
  import {
25
- convertInternalMissingNodeIterator,
26
- convertInternalNode,
27
- convertInternalNodeIterator,
28
- convertInternalNodePromise,
25
+ convertInternalMissingPhotoNodeIterator,
26
+ convertInternalPhotoNode,
27
+ convertInternalPhotoNodeIterator,
28
+ convertInternalPhotoNodePromise,
29
29
  getUid,
30
30
  getUids,
31
31
  } from './transformers';
32
32
  import { DriveAPIService } from './internal/apiService';
33
33
  import { initDownloadModule } from './internal/download';
34
34
  import { DriveEventsService, DriveListener, EventSubscription } from './internal/events';
35
- import { initNodesModule } from './internal/nodes';
36
35
  import {
37
36
  PHOTOS_SHARE_TARGET_TYPES,
38
37
  initPhotosModule,
39
38
  initPhotoSharesModule,
40
39
  initPhotoUploadModule,
40
+ initPhotosNodesModule,
41
41
  } from './internal/photos';
42
42
  import { SDKEvents } from './internal/sdkEvents';
43
43
  import { initSharesModule } from './internal/shares';
@@ -56,7 +56,7 @@ export class ProtonDrivePhotosClient {
56
56
  private sdkEvents: SDKEvents;
57
57
  private events: DriveEventsService;
58
58
  private photoShares: ReturnType<typeof initPhotoSharesModule>;
59
- private nodes: ReturnType<typeof initNodesModule>;
59
+ private nodes: ReturnType<typeof initPhotosNodesModule>;
60
60
  private sharing: ReturnType<typeof initSharingModule>;
61
61
  private download: ReturnType<typeof initDownloadModule>;
62
62
  private upload: ReturnType<typeof initPhotoUploadModule>;
@@ -107,7 +107,7 @@ export class ProtonDrivePhotosClient {
107
107
  cryptoModule,
108
108
  coreShares,
109
109
  );
110
- this.nodes = initNodesModule(
110
+ this.nodes = initPhotosNodesModule(
111
111
  telemetry,
112
112
  apiService,
113
113
  entitiesCache,
@@ -224,9 +224,9 @@ export class ProtonDrivePhotosClient {
224
224
  *
225
225
  * See `ProtonDriveClient.iterateTrashedNodes` for more information.
226
226
  */
227
- async *iterateTrashedNodes(signal?: AbortSignal): AsyncGenerator<MaybeNode> {
227
+ async *iterateTrashedNodes(signal?: AbortSignal): AsyncGenerator<MaybePhotoNode> {
228
228
  this.logger.info('Iterating trashed nodes');
229
- yield* convertInternalNodeIterator(this.nodes.access.iterateTrashedNodes(signal));
229
+ yield * convertInternalPhotoNodeIterator(this.nodes.access.iterateTrashedNodes(signal));
230
230
  }
231
231
 
232
232
  /**
@@ -234,10 +234,10 @@ export class ProtonDrivePhotosClient {
234
234
  *
235
235
  * See `ProtonDriveClient.iterateNodes` for more information.
236
236
  */
237
- async *iterateNodes(nodeUids: NodeOrUid[], signal?: AbortSignal): AsyncGenerator<MaybeMissingNode> {
237
+ async *iterateNodes(nodeUids: NodeOrUid[], signal?: AbortSignal): AsyncGenerator<MaybeMissingPhotoNode> {
238
238
  this.logger.info(`Iterating ${nodeUids.length} nodes`);
239
239
  // TODO: expose photo type
240
- yield* convertInternalMissingNodeIterator(this.nodes.access.iterateNodes(getUids(nodeUids), signal));
240
+ yield * convertInternalMissingPhotoNodeIterator(this.nodes.access.iterateNodes(getUids(nodeUids), signal));
241
241
  }
242
242
 
243
243
  /**
@@ -245,9 +245,9 @@ export class ProtonDrivePhotosClient {
245
245
  *
246
246
  * See `ProtonDriveClient.getNode` for more information.
247
247
  */
248
- async getNode(nodeUid: NodeOrUid): Promise<MaybeNode> {
248
+ async getNode(nodeUid: NodeOrUid): Promise<MaybePhotoNode> {
249
249
  this.logger.info(`Getting node ${getUid(nodeUid)}`);
250
- return convertInternalNodePromise(this.nodes.access.getNode(getUid(nodeUid)));
250
+ return convertInternalPhotoNodePromise(this.nodes.access.getNode(getUid(nodeUid)));
251
251
  }
252
252
 
253
253
  /**
@@ -255,9 +255,9 @@ export class ProtonDrivePhotosClient {
255
255
  *
256
256
  * See `ProtonDriveClient.renameNode` for more information.
257
257
  */
258
- async renameNode(nodeUid: NodeOrUid, newName: string): Promise<MaybeNode> {
258
+ async renameNode(nodeUid: NodeOrUid, newName: string): Promise<MaybePhotoNode> {
259
259
  this.logger.info(`Renaming node ${getUid(nodeUid)}`);
260
- return convertInternalNodePromise(this.nodes.management.renameNode(getUid(nodeUid), newName));
260
+ return convertInternalPhotoNodePromise(this.nodes.management.renameNode(getUid(nodeUid), newName));
261
261
  }
262
262
 
263
263
  /**
@@ -305,9 +305,9 @@ export class ProtonDrivePhotosClient {
305
305
  *
306
306
  * See `ProtonDriveClient.iterateSharedNodes` for more information.
307
307
  */
308
- async *iterateSharedNodes(signal?: AbortSignal): AsyncGenerator<MaybeNode> {
308
+ async *iterateSharedNodes(signal?: AbortSignal): AsyncGenerator<MaybePhotoNode> {
309
309
  this.logger.info('Iterating shared nodes by me');
310
- yield* convertInternalNodeIterator(this.sharing.access.iterateSharedNodes(signal));
310
+ yield * convertInternalPhotoNodeIterator(this.sharing.access.iterateSharedNodes(signal));
311
311
  }
312
312
 
313
313
  /**
@@ -315,11 +315,11 @@ export class ProtonDrivePhotosClient {
315
315
  *
316
316
  * See `ProtonDriveClient.iterateSharedNodesWithMe` for more information.
317
317
  */
318
- async *iterateSharedNodesWithMe(signal?: AbortSignal): AsyncGenerator<MaybeNode> {
318
+ async *iterateSharedNodesWithMe(signal?: AbortSignal): AsyncGenerator<MaybePhotoNode> {
319
319
  this.logger.info('Iterating shared nodes with me');
320
320
 
321
321
  for await (const node of this.sharing.access.iterateSharedNodesWithMe(signal)) {
322
- yield convertInternalNode(node);
322
+ yield convertInternalPhotoNode(node);
323
323
  }
324
324
  }
325
325
 
@@ -482,9 +482,9 @@ export class ProtonDrivePhotosClient {
482
482
  *
483
483
  * The output is not sorted and the order of the nodes is not guaranteed.
484
484
  */
485
- async *iterateAlbums(signal?: AbortSignal): AsyncGenerator<MaybeNode> {
485
+ async *iterateAlbums(signal?: AbortSignal): AsyncGenerator<MaybePhotoNode> {
486
486
  this.logger.info('Iterating albums');
487
487
  // TODO: expose album type
488
- yield* convertInternalNodeIterator(this.photos.albums.iterateAlbums(signal));
488
+ yield * convertInternalPhotoNodeIterator(this.photos.albums.iterateAlbums(signal));
489
489
  }
490
490
  }
@@ -18,6 +18,7 @@ import {
18
18
  UploadMetadata,
19
19
  FileUploader,
20
20
  NodeResult,
21
+ SDKEvent,
21
22
  } from './interface';
22
23
  import { Telemetry } from './telemetry';
23
24
  import {
@@ -27,10 +28,9 @@ import {
27
28
  convertInternalMissingNodeIterator,
28
29
  getUids,
29
30
  } from './transformers';
30
- import { DriveAPIService } from './internal/apiService';
31
31
  import { initDownloadModule } from './internal/download';
32
32
  import { SDKEvents } from './internal/sdkEvents';
33
- import { initSharingPublicModule } from './internal/sharingPublic';
33
+ import { initSharingPublicModule, UnauthDriveAPIService } from './internal/sharingPublic';
34
34
  import { initUploadModule } from './internal/upload';
35
35
 
36
36
  /**
@@ -81,6 +81,7 @@ export class ProtonDrivePublicLinkClient {
81
81
  token,
82
82
  publicShareKey,
83
83
  publicRootNodeUid,
84
+ isAnonymousContext,
84
85
  }: {
85
86
  httpClient: ProtonDriveHTTPClient;
86
87
  account: ProtonDriveAccount;
@@ -92,6 +93,7 @@ export class ProtonDrivePublicLinkClient {
92
93
  token: string;
93
94
  publicShareKey: PrivateKey;
94
95
  publicRootNodeUid: string;
96
+ isAnonymousContext: boolean;
95
97
  }) {
96
98
  if (!telemetry) {
97
99
  telemetry = new Telemetry();
@@ -105,7 +107,7 @@ export class ProtonDrivePublicLinkClient {
105
107
  const fullConfig = getConfig(config);
106
108
  this.sdkEvents = new SDKEvents(telemetry);
107
109
 
108
- const apiService = new DriveAPIService(
110
+ const apiService = new UnauthDriveAPIService(
109
111
  telemetry,
110
112
  this.sdkEvents,
111
113
  httpClient,
@@ -124,6 +126,7 @@ export class ProtonDrivePublicLinkClient {
124
126
  token,
125
127
  publicShareKey,
126
128
  publicRootNodeUid,
129
+ isAnonymousContext,
127
130
  );
128
131
  this.download = initDownloadModule(
129
132
  telemetry,
@@ -133,6 +136,9 @@ export class ProtonDrivePublicLinkClient {
133
136
  this.sharingPublic.shares,
134
137
  this.sharingPublic.nodes.access,
135
138
  this.sharingPublic.nodes.revisions,
139
+ // Ignore manifest integrity verifications for public links.
140
+ // Anonymous user on public page cannot load public keys of other users (yet).
141
+ true,
136
142
  );
137
143
  this.upload = initUploadModule(
138
144
  telemetry,
@@ -159,6 +165,16 @@ export class ProtonDrivePublicLinkClient {
159
165
  };
160
166
  }
161
167
 
168
+ /**
169
+ * Subscribes to the general SDK events.
170
+ *
171
+ * See `ProtonDriveClient.onMessage` for more information.
172
+ */
173
+ onMessage(eventName: SDKEvent, callback: () => void): () => void {
174
+ this.logger.debug(`Subscribing to event ${eventName}`);
175
+ return this.sdkEvents.addListener(eventName, callback);
176
+ }
177
+
162
178
  /**
163
179
  * @returns The root folder to the public link.
164
180
  */
@@ -1,15 +1,19 @@
1
1
  import {
2
2
  MaybeNode as PublicMaybeNode,
3
3
  MaybeMissingNode as PublicMaybeMissingNode,
4
- NodeEntity as PublicNodeEntity,
5
4
  DegradedNode as PublicDegradedNode,
6
5
  Revision as PublicRevision,
7
6
  Result,
8
7
  resultOk,
9
8
  resultError,
10
9
  MissingNode,
10
+ MaybePhotoNode as PublicMaybePhotoNode,
11
+ MaybeMissingPhotoNode as PublicMaybeMissingPhotoNode,
12
+ PhotoNode as PublicPhotoNode,
13
+ DegradedPhotoNode as PublicDegradedPhotoNode,
11
14
  } from './interface';
12
15
  import { DecryptedNode as InternalNode, DecryptedRevision as InternalRevision } from './internal/nodes';
16
+ import { DecryptedPhotoNode as InternalPartialPhotoNode } from './internal/photos';
13
17
 
14
18
  type InternalPartialNode = Pick<
15
19
  InternalNode,
@@ -25,6 +29,7 @@ type InternalPartialNode = Pick<
25
29
  | 'isShared'
26
30
  | 'isSharedPublicly'
27
31
  | 'creationTime'
32
+ | 'modificationTime'
28
33
  | 'trashTime'
29
34
  | 'activeRevision'
30
35
  | 'folder'
@@ -93,6 +98,7 @@ export function convertInternalNode(node: InternalPartialNode): PublicMaybeNode
93
98
  isShared: node.isShared,
94
99
  isSharedPublicly: node.isSharedPublicly,
95
100
  creationTime: node.creationTime,
101
+ modificationTime: node.modificationTime,
96
102
  trashTime: node.trashTime,
97
103
  totalStorageSize: node.totalStorageSize,
98
104
  folder: node.folder,
@@ -118,7 +124,48 @@ export function convertInternalNode(node: InternalPartialNode): PublicMaybeNode
118
124
  ...baseNodeMetadata,
119
125
  name: name.value,
120
126
  activeRevision: activeRevision?.ok ? convertInternalRevision(activeRevision.value) : undefined,
121
- } as PublicNodeEntity);
127
+ });
128
+ }
129
+
130
+ export async function* convertInternalPhotoNodeIterator(
131
+ photoNodeIterator: AsyncGenerator<InternalPartialPhotoNode>,
132
+ ): AsyncGenerator<PublicMaybePhotoNode> {
133
+ for await (const photoNode of photoNodeIterator) {
134
+ yield convertInternalPhotoNode(photoNode);
135
+ }
136
+ }
137
+
138
+ export async function* convertInternalMissingPhotoNodeIterator(
139
+ photoNodeIterator: AsyncGenerator<InternalPartialPhotoNode | MissingNode>,
140
+ ): AsyncGenerator<PublicMaybeMissingPhotoNode> {
141
+ for await (const photoNode of photoNodeIterator) {
142
+ if ('missingUid' in photoNode) {
143
+ yield resultError(photoNode);
144
+ } else {
145
+ yield convertInternalPhotoNode(photoNode);
146
+ }
147
+ }
148
+ }
149
+
150
+ export async function convertInternalPhotoNodePromise(
151
+ photoNodePromise: Promise<InternalPartialPhotoNode>,
152
+ ): Promise<PublicMaybePhotoNode> {
153
+ const photoNode = await photoNodePromise;
154
+ return convertInternalPhotoNode(photoNode);
155
+ }
156
+
157
+ export function convertInternalPhotoNode(photoNode: InternalPartialPhotoNode): PublicMaybePhotoNode {
158
+ const node = convertInternalNode(photoNode);
159
+ if (node.ok) {
160
+ return resultOk({
161
+ ...node.value,
162
+ photo: photoNode.photo,
163
+ } as PublicPhotoNode);
164
+ }
165
+ return resultError({
166
+ ...node.error,
167
+ photo: photoNode.photo,
168
+ } as PublicDegradedPhotoNode);
122
169
  }
123
170
 
124
171
  export async function* convertInternalRevisionIterator(