@protontech/drive-sdk 0.3.1 → 0.3.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.
Files changed (86) hide show
  1. package/dist/crypto/driveCrypto.d.ts +1 -1
  2. package/dist/crypto/driveCrypto.js.map +1 -1
  3. package/dist/crypto/interface.d.ts +1 -1
  4. package/dist/crypto/openPGPCrypto.d.ts +1 -1
  5. package/dist/crypto/openPGPCrypto.js +4 -1
  6. package/dist/crypto/openPGPCrypto.js.map +1 -1
  7. package/dist/internal/download/cryptoService.js +2 -2
  8. package/dist/internal/download/cryptoService.js.map +1 -1
  9. package/dist/internal/download/fileDownloader.js +2 -2
  10. package/dist/internal/download/fileDownloader.js.map +1 -1
  11. package/dist/internal/download/fileDownloader.test.js +3 -1
  12. package/dist/internal/download/fileDownloader.test.js.map +1 -1
  13. package/dist/internal/nodes/cache.js +3 -1
  14. package/dist/internal/nodes/cache.js.map +1 -1
  15. package/dist/internal/nodes/cryptoReporter.d.ts +20 -0
  16. package/dist/internal/nodes/cryptoReporter.js +96 -0
  17. package/dist/internal/nodes/cryptoReporter.js.map +1 -0
  18. package/dist/internal/nodes/cryptoService.d.ts +17 -12
  19. package/dist/internal/nodes/cryptoService.js +17 -97
  20. package/dist/internal/nodes/cryptoService.js.map +1 -1
  21. package/dist/internal/nodes/cryptoService.test.js +3 -1
  22. package/dist/internal/nodes/cryptoService.test.js.map +1 -1
  23. package/dist/internal/nodes/index.js +3 -1
  24. package/dist/internal/nodes/index.js.map +1 -1
  25. package/dist/internal/nodes/interface.d.ts +1 -1
  26. package/dist/internal/nodes/nodesAccess.d.ts +2 -2
  27. package/dist/internal/nodes/nodesAccess.js +52 -54
  28. package/dist/internal/nodes/nodesAccess.js.map +1 -1
  29. package/dist/internal/sharing/cache.d.ts +3 -0
  30. package/dist/internal/sharing/cache.js +17 -2
  31. package/dist/internal/sharing/cache.js.map +1 -1
  32. package/dist/internal/sharing/interface.d.ts +1 -1
  33. package/dist/internal/sharing/interface.js +1 -1
  34. package/dist/internal/sharing/sharingAccess.js +6 -0
  35. package/dist/internal/sharing/sharingAccess.js.map +1 -1
  36. package/dist/internal/sharing/sharingAccess.test.js +242 -33
  37. package/dist/internal/sharing/sharingAccess.test.js.map +1 -1
  38. package/dist/internal/sharingPublic/apiService.d.ts +1 -1
  39. package/dist/internal/sharingPublic/apiService.js +9 -2
  40. package/dist/internal/sharingPublic/apiService.js.map +1 -1
  41. package/dist/internal/sharingPublic/cryptoService.d.ts +6 -20
  42. package/dist/internal/sharingPublic/cryptoService.js +40 -103
  43. package/dist/internal/sharingPublic/cryptoService.js.map +1 -1
  44. package/dist/internal/sharingPublic/index.d.ts +2 -2
  45. package/dist/internal/sharingPublic/index.js +2 -2
  46. package/dist/internal/sharingPublic/index.js.map +1 -1
  47. package/dist/internal/sharingPublic/interface.d.ts +1 -43
  48. package/dist/internal/sharingPublic/manager.d.ts +1 -1
  49. package/dist/internal/sharingPublic/manager.js +9 -7
  50. package/dist/internal/sharingPublic/manager.js.map +1 -1
  51. package/dist/internal/upload/streamUploader.js +1 -1
  52. package/dist/internal/upload/streamUploader.js.map +1 -1
  53. package/dist/internal/upload/streamUploader.test.js +3 -1
  54. package/dist/internal/upload/streamUploader.test.js.map +1 -1
  55. package/dist/protonDriveClient.js +1 -0
  56. package/dist/protonDriveClient.js.map +1 -1
  57. package/dist/protonDrivePublicLinkClient.d.ts +13 -4
  58. package/dist/protonDrivePublicLinkClient.js +13 -11
  59. package/dist/protonDrivePublicLinkClient.js.map +1 -1
  60. package/package.json +1 -1
  61. package/src/crypto/driveCrypto.ts +1 -1
  62. package/src/crypto/interface.ts +1 -1
  63. package/src/crypto/openPGPCrypto.ts +5 -2
  64. package/src/internal/download/cryptoService.ts +2 -2
  65. package/src/internal/download/fileDownloader.test.ts +3 -1
  66. package/src/internal/download/fileDownloader.ts +2 -2
  67. package/src/internal/nodes/cache.ts +3 -1
  68. package/src/internal/nodes/cryptoReporter.ts +145 -0
  69. package/src/internal/nodes/cryptoService.test.ts +3 -1
  70. package/src/internal/nodes/cryptoService.ts +44 -137
  71. package/src/internal/nodes/index.ts +3 -1
  72. package/src/internal/nodes/interface.ts +1 -1
  73. package/src/internal/nodes/nodesAccess.ts +59 -61
  74. package/src/internal/sharing/cache.ts +19 -2
  75. package/src/internal/sharing/interface.ts +1 -1
  76. package/src/internal/sharing/sharingAccess.test.ts +282 -34
  77. package/src/internal/sharing/sharingAccess.ts +6 -0
  78. package/src/internal/sharingPublic/apiService.ts +11 -2
  79. package/src/internal/sharingPublic/cryptoService.ts +71 -135
  80. package/src/internal/sharingPublic/index.ts +3 -2
  81. package/src/internal/sharingPublic/interface.ts +8 -53
  82. package/src/internal/sharingPublic/manager.ts +9 -8
  83. package/src/internal/upload/streamUploader.test.ts +3 -1
  84. package/src/internal/upload/streamUploader.ts +1 -1
  85. package/src/protonDriveClient.ts +1 -0
  86. package/src/protonDrivePublicLinkClient.ts +26 -12
@@ -1,5 +1,5 @@
1
1
  import { DriveAPIService, drivePaths, nodeTypeNumberToNodeType } from '../apiService';
2
- import { Logger } from '../../interface';
2
+ import { Logger, MemberRole } from '../../interface';
3
3
  import { makeNodeUid, splitNodeUid } from '../uids';
4
4
  import { EncryptedShareCrypto, EncryptedNode } from './interface';
5
5
 
@@ -42,13 +42,14 @@ export class SharingPublicAPIService {
42
42
  };
43
43
  }
44
44
 
45
- async *iterateChildren(parentUid: string): AsyncGenerator<EncryptedNode> {
45
+ async *iterateFolderChildren(parentUid: string, signal?: AbortSignal): AsyncGenerator<EncryptedNode> {
46
46
  const { volumeId: token, nodeId } = splitNodeUid(parentUid);
47
47
 
48
48
  let page = 0;
49
49
  while (true) {
50
50
  const response = await this.apiService.get<GetTokenFolderChildrenResponse>(
51
51
  `drive/urls/${token}/folders/${nodeId}/children?Page=${page}&PageSize=${PAGE_SIZE}`,
52
+ signal,
52
53
  );
53
54
 
54
55
  for (const link of response.Links) {
@@ -72,6 +73,10 @@ function tokenToEncryptedNode(logger: Logger, token: GetTokenInfoResponse['Token
72
73
  uid: makeNodeUid(token.Token, token.LinkID),
73
74
  parentUid: undefined,
74
75
  type: nodeTypeNumberToNodeType(logger, token.LinkType),
76
+ creationTime: new Date(), // TODO
77
+
78
+ isShared: false,
79
+ directRole: MemberRole.Viewer, // TODO
75
80
  };
76
81
 
77
82
  const baseCryptoNodeMetadata = {
@@ -124,7 +129,11 @@ function linkToEncryptedNode(
124
129
  uid: makeNodeUid(token, link.LinkID),
125
130
  parentUid: link.ParentLinkID ? makeNodeUid(token, link.ParentLinkID) : undefined,
126
131
  type: nodeTypeNumberToNodeType(logger, link.Type),
132
+ creationTime: new Date(), // TODO
127
133
  totalStorageSize: link.TotalSize,
134
+
135
+ isShared: false,
136
+ directRole: MemberRole.Viewer, // TODO
128
137
  };
129
138
 
130
139
  const baseCryptoNodeMetadata = {
@@ -1,26 +1,34 @@
1
- import { DriveCrypto, PrivateKey } from '../../crypto';
2
- import { resultOk, resultError, Result } from '../../interface';
3
- import { getErrorMessage } from '../errors';
4
- import { EncryptedShareCrypto, EncryptedNode, DecryptedNode, DecryptedNodeKeys } from './interface';
5
-
6
- /**
7
- * Provides crypto operations for public link data.
8
- *
9
- * The public link crypto service is responsible for decrypting and encrypting
10
- * public link data. It should export high-level actions only, such as "decrypt
11
- * share key" instead of low-level operations like "decrypt key". Low-level
12
- * operations should be kept private to the module.
13
- */
14
- export class SharingPublicCryptoService {
1
+ import { c } from 'ttag';
2
+
3
+ import { DriveCrypto, PrivateKey, VERIFICATION_STATUS } from '../../crypto';
4
+ import { getVerificationMessage } from '../errors';
5
+ import {
6
+ resultOk,
7
+ resultError,
8
+ Author,
9
+ AnonymousUser,
10
+ ProtonDriveTelemetry,
11
+ MetricVerificationErrorField,
12
+ MetricVolumeType,
13
+ MetricsDecryptionErrorField,
14
+ Logger,
15
+ ProtonDriveAccount,
16
+ } from '../../interface';
17
+ import { NodesCryptoService } from '../nodes/cryptoService';
18
+ import { EncryptedShareCrypto } from './interface';
19
+
20
+ export class SharingPublicCryptoService extends NodesCryptoService {
15
21
  constructor(
16
- private driveCrypto: DriveCrypto,
22
+ telemetry: ProtonDriveTelemetry,
23
+ driveCrypto: DriveCrypto,
24
+ account: ProtonDriveAccount,
17
25
  private password: string,
18
26
  ) {
19
- this.driveCrypto = driveCrypto;
27
+ super(telemetry, driveCrypto, account, new SharingPublicCryptoReporter(telemetry));
20
28
  this.password = password;
21
29
  }
22
30
 
23
- async decryptShareKey(encryptedShare: EncryptedShareCrypto): Promise<PrivateKey> {
31
+ async decryptPublicLinkShareKey(encryptedShare: EncryptedShareCrypto): Promise<PrivateKey> {
24
32
  const { key: shareKey } = await this.driveCrypto.decryptKeyWithSrpPassword(
25
33
  this.password,
26
34
  encryptedShare.base64UrlPasswordSalt,
@@ -29,134 +37,62 @@ export class SharingPublicCryptoService {
29
37
  );
30
38
  return shareKey;
31
39
  }
40
+ }
32
41
 
33
- // TODO: verfiy it has all needed
34
- async decryptNode(
35
- node: EncryptedNode,
36
- parentKey: PrivateKey,
37
- ): Promise<{ node: DecryptedNode; keys?: DecryptedNodeKeys }> {
38
- const commonNodeMetadata = {
39
- ...node,
40
- encryptedCrypto: undefined,
41
- };
42
-
43
- const { name } = await this.decryptName(node, parentKey);
44
-
45
- let passphrase, key, passphraseSessionKey;
46
- try {
47
- const keyResult = await this.decryptKey(node, parentKey);
48
- passphrase = keyResult.passphrase;
49
- key = keyResult.key;
50
- passphraseSessionKey = keyResult.passphraseSessionKey;
51
- } catch (error: unknown) {
52
- return {
53
- node: {
54
- ...commonNodeMetadata,
55
- name,
56
- errors: [error],
57
- },
58
- };
59
- }
60
-
61
- const errors = [];
62
-
63
- let hashKey;
64
- if ('folder' in node.encryptedCrypto) {
65
- try {
66
- const hashKeyResult = await this.decryptHashKey(node, key);
67
- hashKey = hashKeyResult.hashKey;
68
- } catch (error: unknown) {
69
- errors.push(error);
70
- }
71
- }
42
+ class SharingPublicCryptoReporter {
43
+ private logger: Logger;
44
+ private telemetry: ProtonDriveTelemetry;
72
45
 
73
- let contentKeyPacketSessionKey;
74
- if ('file' in node.encryptedCrypto) {
75
- try {
76
- const keySessionKeyResult = await this.driveCrypto.decryptAndVerifySessionKey(
77
- node.encryptedCrypto.file.base64ContentKeyPacket,
78
- '',
79
- key,
80
- [],
81
- );
46
+ constructor(telemetry: ProtonDriveTelemetry) {
47
+ this.telemetry = telemetry;
48
+ this.logger = telemetry.getLogger('sharingPublic-crypto');
49
+ }
82
50
 
83
- contentKeyPacketSessionKey = keySessionKeyResult.sessionKey;
84
- } catch (error: unknown) {
85
- errors.push(error);
86
- }
51
+ async handleClaimedAuthor(
52
+ node: { uid: string; creationTime: Date },
53
+ field: MetricVerificationErrorField,
54
+ signatureType: string,
55
+ verified: VERIFICATION_STATUS,
56
+ verificationErrors?: Error[],
57
+ claimedAuthor?: string,
58
+ notAvailableVerificationKeys = false,
59
+ ): Promise<Author> {
60
+ if (verified === VERIFICATION_STATUS.SIGNED_AND_VALID) {
61
+ return resultOk(claimedAuthor || (null as AnonymousUser));
87
62
  }
88
63
 
89
- return {
90
- node: {
91
- ...commonNodeMetadata,
92
- name,
93
- errors: errors.length ? errors : undefined,
94
- },
95
- keys: {
96
- passphrase,
97
- key,
98
- passphraseSessionKey,
99
- contentKeyPacketSessionKey,
100
- hashKey,
101
- },
102
- };
64
+ return resultError({
65
+ claimedAuthor,
66
+ error: !claimedAuthor
67
+ ? c('Info').t`Author is not provided on public link`
68
+ : getVerificationMessage(verified, verificationErrors, signatureType, notAvailableVerificationKeys),
69
+ });
103
70
  }
104
71
 
105
- private async decryptKey(node: EncryptedNode, parentKey: PrivateKey): Promise<DecryptedNodeKeys> {
106
- const key = await this.driveCrypto.decryptKey(
107
- node.encryptedCrypto.armoredKey,
108
- node.encryptedCrypto.armoredNodePassphrase,
109
- '',
110
- [parentKey],
111
- [],
112
- );
113
-
114
- return {
115
- passphrase: key.passphrase,
116
- key: key.key,
117
- passphraseSessionKey: key.passphraseSessionKey,
118
- };
119
- }
72
+ reportDecryptionError(
73
+ node: { uid: string; creationTime: Date },
74
+ field: MetricsDecryptionErrorField,
75
+ error: unknown,
76
+ ) {
77
+ const fromBefore2024 = node.creationTime < new Date('2024-01-01');
120
78
 
121
- private async decryptName(
122
- node: EncryptedNode,
123
- parentKey: PrivateKey,
124
- ): Promise<{
125
- name: Result<string, Error>;
126
- }> {
127
- try {
128
- const { name } = await this.driveCrypto.decryptNodeName(node.encryptedName, parentKey, []);
79
+ this.logger.error(
80
+ `Failed to decrypt public link node ${node.uid} (from before 2024: ${fromBefore2024})`,
81
+ error,
82
+ );
129
83
 
130
- return {
131
- name: resultOk(name),
132
- };
133
- } catch (error: unknown) {
134
- const errorMessage = getErrorMessage(error);
135
- return {
136
- name: resultError(new Error(errorMessage)),
137
- };
138
- }
84
+ this.telemetry.recordMetric({
85
+ eventName: 'decryptionError',
86
+ volumeType: MetricVolumeType.SharedPublic,
87
+ field,
88
+ fromBefore2024,
89
+ error,
90
+ uid: node.uid,
91
+ });
139
92
  }
140
93
 
141
- private async decryptHashKey(
142
- node: EncryptedNode,
143
- nodeKey: PrivateKey,
144
- ): Promise<{
145
- hashKey: Uint8Array;
146
- }> {
147
- if (!('folder' in node.encryptedCrypto)) {
148
- // This is developer error.
149
- throw new Error('Node is not a folder');
150
- }
151
-
152
- const { hashKey } = await this.driveCrypto.decryptNodeHashKey(
153
- node.encryptedCrypto.folder.armoredHashKey,
154
- nodeKey,
155
- [],
156
- );
157
-
158
- return {
159
- hashKey,
160
- };
94
+ reportVerificationError() {
95
+ // Authors or signatures are not provided on public links.
96
+ // We do not report any signature verification errors at this moment.
161
97
  }
162
98
  }
@@ -1,5 +1,5 @@
1
1
  import { DriveCrypto } from '../../crypto';
2
- import { ProtonDriveCryptoCache, ProtonDriveTelemetry } from '../../interface';
2
+ import { ProtonDriveCryptoCache, ProtonDriveTelemetry, ProtonDriveAccount } from '../../interface';
3
3
  import { DriveAPIService } from '../apiService';
4
4
  import { SharingPublicAPIService } from './apiService';
5
5
  import { SharingPublicCryptoCache } from './cryptoCache';
@@ -22,12 +22,13 @@ export function initSharingPublicModule(
22
22
  apiService: DriveAPIService,
23
23
  driveCryptoCache: ProtonDriveCryptoCache,
24
24
  driveCrypto: DriveCrypto,
25
+ account: ProtonDriveAccount,
25
26
  token: string,
26
27
  password: string,
27
28
  ) {
28
29
  const api = new SharingPublicAPIService(telemetry.getLogger('sharingPublic-api'), apiService);
29
30
  const cryptoCache = new SharingPublicCryptoCache(telemetry.getLogger('sharingPublic-crypto'), driveCryptoCache);
30
- const cryptoService = new SharingPublicCryptoService(driveCrypto, password);
31
+ const cryptoService = new SharingPublicCryptoService(telemetry, driveCrypto, account, password);
31
32
  const manager = new SharingPublicManager(
32
33
  telemetry.getLogger('sharingPublic-nodes'),
33
34
  api,
@@ -1,59 +1,14 @@
1
- import { PrivateKey, SessionKey } from "../../crypto";
2
- import { NodeType, Result, InvalidNameError } from "../../interface";
1
+ // TODO: use them directly, or avoid them completely
2
+ export type {
3
+ EncryptedNode,
4
+ EncryptedNodeFolderCrypto,
5
+ EncryptedNodeFileCrypto,
6
+ DecryptedNode,
7
+ DecryptedNodeKeys,
8
+ } from '../nodes/interface';
3
9
 
4
10
  export interface EncryptedShareCrypto {
5
11
  base64UrlPasswordSalt: string;
6
12
  armoredKey: string;
7
13
  armoredPassphrase: string;
8
14
  }
9
-
10
- // TODO: reuse node entity, or keep custom?
11
- interface BaseNode {
12
- // Internal metadata
13
- hash?: string; // root node doesn't have any hash
14
- encryptedName: string;
15
-
16
- // Basic node metadata
17
- uid: string;
18
- parentUid?: string;
19
- type: NodeType;
20
- mediaType?: string;
21
- totalStorageSize?: number;
22
- }
23
-
24
- export interface EncryptedNode extends BaseNode {
25
- encryptedCrypto: EncryptedNodeFolderCrypto | EncryptedNodeFileCrypto;
26
- }
27
-
28
- export interface EncryptedNodeCrypto {
29
- signatureEmail?: string;
30
- armoredKey: string;
31
- armoredNodePassphrase: string;
32
- armoredNodePassphraseSignature?: string;
33
- }
34
-
35
- export interface EncryptedNodeFileCrypto extends EncryptedNodeCrypto {
36
- file: {
37
- base64ContentKeyPacket: string;
38
- };
39
- }
40
-
41
- export interface EncryptedNodeFolderCrypto extends EncryptedNodeCrypto {
42
- folder: {
43
- armoredExtendedAttributes?: string;
44
- armoredHashKey: string;
45
- };
46
- }
47
-
48
- export interface DecryptedNode extends BaseNode {
49
- name: Result<string, Error | InvalidNameError>;
50
- errors?: unknown[];
51
- }
52
-
53
- export interface DecryptedNodeKeys {
54
- passphrase: string;
55
- key: PrivateKey;
56
- passphraseSessionKey: SessionKey;
57
- contentKeyPacketSessionKey?: SessionKey;
58
- hashKey?: Uint8Array;
59
- }
@@ -1,5 +1,6 @@
1
1
  import { PrivateKey } from '../../crypto';
2
2
  import { Logger } from '../../interface';
3
+ import { parseNode } from '../nodes/nodesAccess';
3
4
  import { SharingPublicAPIService } from './apiService';
4
5
  import { SharingPublicCryptoCache } from './cryptoCache';
5
6
  import { SharingPublicCryptoService } from './cryptoService';
@@ -27,35 +28,35 @@ export class SharingPublicManager {
27
28
  return this.decryptNode(encryptedNode);
28
29
  }
29
30
 
30
- async *iterateChildren(parentUid: string): AsyncGenerator<DecryptedNode> {
31
+ async *iterateFolderChildren(parentUid: string, signal?: AbortSignal): AsyncGenerator<DecryptedNode> {
31
32
  // TODO: optimise this - decrypt in parallel
32
- for await (const node of this.api.iterateChildren(parentUid)) {
33
+ for await (const node of this.api.iterateFolderChildren(parentUid, signal)) {
33
34
  const decryptedNode = await this.decryptNode(node);
34
35
  yield decryptedNode;
35
36
  }
36
37
  }
37
38
 
38
39
  private async decryptShare(encryptedShare: EncryptedShareCrypto): Promise<void> {
39
- const shareKey = await this.cryptoService.decryptShareKey(encryptedShare);
40
+ const shareKey = await this.cryptoService.decryptPublicLinkShareKey(encryptedShare);
40
41
  await this.cryptoCache.setShareKey(shareKey);
41
42
  }
42
43
 
43
44
  private async decryptNode(encryptedNode: EncryptedNode): Promise<DecryptedNode> {
44
45
  const parentKey = await this.getParentKey(encryptedNode);
45
46
 
46
- const { node: decryptedNode, keys } = await this.cryptoService.decryptNode(encryptedNode, parentKey);
47
+ const { node: unparsedNode, keys } = await this.cryptoService.decryptNode(encryptedNode, parentKey);
48
+ const node = await parseNode(this.logger, unparsedNode);
47
49
 
48
50
  // TODO: cache of metadata?
49
-
50
51
  if (keys) {
51
52
  try {
52
- await this.cryptoCache.setNodeKeys(decryptedNode.uid, keys);
53
+ await this.cryptoCache.setNodeKeys(node.uid, keys);
53
54
  } catch (error: unknown) {
54
- this.logger.error(`Failed to cache node keys ${decryptedNode.uid}`, error);
55
+ this.logger.error(`Failed to cache node keys ${node.uid}`, error);
55
56
  }
56
57
  }
57
58
 
58
- return decryptedNode;
59
+ return node;
59
60
  }
60
61
 
61
62
  private async getParentKey(node: Pick<DecryptedNode, 'parentUid'>): Promise<PrivateKey> {
@@ -189,8 +189,10 @@ describe('StreamUploader', () => {
189
189
 
190
190
  const verifyOnProgress = async (uploadedBytes: number[]) => {
191
191
  expect(onProgress).toHaveBeenCalledTimes(uploadedBytes.length);
192
+ let fileProgress = 0;
192
193
  for (let i = 0; i < uploadedBytes.length; i++) {
193
- expect(onProgress).toHaveBeenNthCalledWith(i + 1, uploadedBytes[i]);
194
+ fileProgress += uploadedBytes[i];
195
+ expect(onProgress).toHaveBeenNthCalledWith(i + 1, fileProgress);
194
196
  }
195
197
  };
196
198
 
@@ -122,7 +122,7 @@ export class StreamUploader {
122
122
  this.logger.info(`Starting upload`);
123
123
  await this.encryptAndUploadBlocks(stream, thumbnails, (uploadedBytes) => {
124
124
  fileProgress += uploadedBytes;
125
- onProgress?.(uploadedBytes);
125
+ onProgress?.(fileProgress);
126
126
  });
127
127
 
128
128
  this.logger.debug(`All blocks uploaded, committing`);
@@ -208,6 +208,7 @@ export class ProtonDriveClient {
208
208
  return new ProtonDrivePublicLinkClient({
209
209
  httpClient,
210
210
  cryptoCache,
211
+ account,
211
212
  openPGPCryptoModule,
212
213
  srpModule,
213
214
  config,
@@ -7,9 +7,11 @@ import {
7
7
  Logger,
8
8
  ProtonDriveCryptoCache,
9
9
  NodeOrUid,
10
+ ProtonDriveAccount,
11
+ MaybeNode,
10
12
  } from './interface';
11
13
  import { Telemetry } from './telemetry';
12
- import { getUid } from './transformers';
14
+ import { getUid, convertInternalNodePromise, convertInternalNodeIterator } from './transformers';
13
15
  import { DriveAPIService } from './internal/apiService';
14
16
  import { SDKEvents } from './internal/sdkEvents';
15
17
  import { initSharingPublicModule } from './internal/sharingPublic';
@@ -52,6 +54,7 @@ export class ProtonDrivePublicLinkClient {
52
54
  constructor({
53
55
  httpClient,
54
56
  cryptoCache,
57
+ account,
55
58
  openPGPCryptoModule,
56
59
  srpModule,
57
60
  config,
@@ -61,6 +64,7 @@ export class ProtonDrivePublicLinkClient {
61
64
  }: {
62
65
  httpClient: ProtonDriveHTTPClient;
63
66
  cryptoCache: ProtonDriveCryptoCache;
67
+ account: ProtonDriveAccount;
64
68
  openPGPCryptoModule: OpenPGPCrypto;
65
69
  srpModule: SRPModule;
66
70
  config?: ProtonDriveConfig;
@@ -84,7 +88,15 @@ export class ProtonDrivePublicLinkClient {
84
88
  fullConfig.language,
85
89
  );
86
90
  const driveCrypto = new DriveCrypto(openPGPCryptoModule, srpModule);
87
- this.sharingPublic = initSharingPublicModule(telemetry, apiService, cryptoCache, driveCrypto, token, password);
91
+ this.sharingPublic = initSharingPublicModule(
92
+ telemetry,
93
+ apiService,
94
+ cryptoCache,
95
+ driveCrypto,
96
+ account,
97
+ token,
98
+ password,
99
+ );
88
100
 
89
101
  this.experimental = {
90
102
  getNodeUrl: async (nodeUid: NodeOrUid) => {
@@ -103,19 +115,21 @@ export class ProtonDrivePublicLinkClient {
103
115
  };
104
116
  }
105
117
 
106
- // TODO: comment
107
- // TODO: add public node interface
108
- async getRootNode() {
118
+ /**
119
+ * @returns The root folder to the public link.
120
+ */
121
+ async getRootNode(): Promise<MaybeNode> {
109
122
  this.logger.info(`Getting root node`);
110
- // TODO: conversion to public node
111
- return this.sharingPublic.getRootNode();
123
+ return convertInternalNodePromise(this.sharingPublic.getRootNode());
112
124
  }
113
125
 
114
- // TODO: comment
115
- // TODO: add public node interface
116
- async *iterateChildren(parentUid: NodeOrUid) {
126
+ /**
127
+ * Iterates the children of the given parent node.
128
+ *
129
+ * See `ProtonDriveClient.iterateFolderChildren` for more information.
130
+ */
131
+ async *iterateFolderChildren(parentUid: NodeOrUid, signal?: AbortSignal): AsyncGenerator<MaybeNode> {
117
132
  this.logger.info(`Iterating children of ${getUid(parentUid)}`);
118
- // TODO: conversion to public node
119
- yield * this.sharingPublic.iterateChildren(getUid(parentUid));
133
+ yield * convertInternalNodeIterator(this.sharingPublic.iterateFolderChildren(getUid(parentUid), signal));
120
134
  }
121
135
  }