@protontech/drive-sdk 0.2.1 → 0.3.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 (165) hide show
  1. package/dist/crypto/interface.d.ts +5 -0
  2. package/dist/diagnostic/httpClient.d.ts +3 -3
  3. package/dist/diagnostic/interface.d.ts +26 -29
  4. package/dist/diagnostic/sdkDiagnostic.js +50 -24
  5. package/dist/diagnostic/sdkDiagnostic.js.map +1 -1
  6. package/dist/errors.d.ts +3 -3
  7. package/dist/errors.js +7 -7
  8. package/dist/errors.js.map +1 -1
  9. package/dist/interface/author.d.ts +1 -1
  10. package/dist/interface/events.d.ts +1 -1
  11. package/dist/interface/events.js.map +1 -1
  12. package/dist/interface/httpClient.d.ts +5 -5
  13. package/dist/interface/index.d.ts +15 -5
  14. package/dist/internal/apiService/apiService.js +12 -4
  15. package/dist/internal/apiService/apiService.js.map +1 -1
  16. package/dist/internal/apiService/errorCodes.d.ts +1 -0
  17. package/dist/internal/apiService/errorCodes.js.map +1 -1
  18. package/dist/internal/apiService/errors.d.ts +4 -3
  19. package/dist/internal/apiService/errors.js +7 -4
  20. package/dist/internal/apiService/errors.js.map +1 -1
  21. package/dist/internal/apiService/errors.test.js +2 -1
  22. package/dist/internal/apiService/errors.test.js.map +1 -1
  23. package/dist/internal/events/index.d.ts +1 -1
  24. package/dist/internal/nodes/apiService.js +3 -0
  25. package/dist/internal/nodes/apiService.js.map +1 -1
  26. package/dist/internal/nodes/apiService.test.js +18 -0
  27. package/dist/internal/nodes/apiService.test.js.map +1 -1
  28. package/dist/internal/nodes/cryptoCache.js +6 -7
  29. package/dist/internal/nodes/cryptoCache.js.map +1 -1
  30. package/dist/internal/nodes/cryptoCache.test.js +4 -7
  31. package/dist/internal/nodes/cryptoCache.test.js.map +1 -1
  32. package/dist/internal/nodes/cryptoService.js +44 -20
  33. package/dist/internal/nodes/cryptoService.js.map +1 -1
  34. package/dist/internal/nodes/nodesAccess.js +2 -2
  35. package/dist/internal/nodes/nodesAccess.js.map +1 -1
  36. package/dist/internal/nodes/nodesManagement.js +0 -2
  37. package/dist/internal/nodes/nodesManagement.js.map +1 -1
  38. package/dist/internal/shares/cryptoCache.d.ts +4 -3
  39. package/dist/internal/shares/cryptoCache.js +23 -6
  40. package/dist/internal/shares/cryptoCache.js.map +1 -1
  41. package/dist/internal/shares/cryptoCache.test.js +3 -2
  42. package/dist/internal/shares/cryptoCache.test.js.map +1 -1
  43. package/dist/internal/shares/index.js +1 -1
  44. package/dist/internal/shares/index.js.map +1 -1
  45. package/dist/internal/sharing/cryptoService.js +8 -6
  46. package/dist/internal/sharing/cryptoService.js.map +1 -1
  47. package/dist/internal/sharing/cryptoService.test.js +13 -0
  48. package/dist/internal/sharing/cryptoService.test.js.map +1 -1
  49. package/dist/internal/sharing/index.js +1 -1
  50. package/dist/internal/sharing/index.js.map +1 -1
  51. package/dist/internal/sharing/interface.d.ts +0 -4
  52. package/dist/internal/sharing/sharingAccess.d.ts +1 -0
  53. package/dist/internal/sharing/sharingAccess.js +6 -1
  54. package/dist/internal/sharing/sharingAccess.js.map +1 -1
  55. package/dist/internal/sharing/sharingAccess.test.js +3 -3
  56. package/dist/internal/sharing/sharingAccess.test.js.map +1 -1
  57. package/dist/internal/sharing/sharingManagement.d.ts +3 -1
  58. package/dist/internal/sharing/sharingManagement.js +37 -17
  59. package/dist/internal/sharing/sharingManagement.js.map +1 -1
  60. package/dist/internal/sharing/sharingManagement.test.js +61 -14
  61. package/dist/internal/sharing/sharingManagement.test.js.map +1 -1
  62. package/dist/internal/sharingPublic/apiService.d.ts +19 -0
  63. package/dist/internal/sharingPublic/apiService.js +134 -0
  64. package/dist/internal/sharingPublic/apiService.js.map +1 -0
  65. package/dist/internal/sharingPublic/cryptoCache.d.ts +19 -0
  66. package/dist/internal/sharingPublic/cryptoCache.js +72 -0
  67. package/dist/internal/sharingPublic/cryptoCache.js.map +1 -0
  68. package/dist/internal/sharingPublic/cryptoService.d.ts +23 -0
  69. package/dist/internal/sharingPublic/cryptoService.js +120 -0
  70. package/dist/internal/sharingPublic/cryptoService.js.map +1 -0
  71. package/dist/internal/sharingPublic/index.d.ts +15 -0
  72. package/dist/internal/sharingPublic/index.js +27 -0
  73. package/dist/internal/sharingPublic/index.js.map +1 -0
  74. package/dist/internal/sharingPublic/interface.d.ts +48 -0
  75. package/dist/internal/sharingPublic/interface.js +3 -0
  76. package/dist/internal/sharingPublic/interface.js.map +1 -0
  77. package/dist/internal/sharingPublic/manager.d.ts +19 -0
  78. package/dist/internal/sharingPublic/manager.js +79 -0
  79. package/dist/internal/sharingPublic/manager.js.map +1 -0
  80. package/dist/internal/sharingPublic/session/apiService.d.ts +28 -0
  81. package/dist/internal/sharingPublic/session/apiService.js +55 -0
  82. package/dist/internal/sharingPublic/session/apiService.js.map +1 -0
  83. package/dist/internal/sharingPublic/session/httpClient.d.ts +16 -0
  84. package/dist/internal/sharingPublic/session/httpClient.js +41 -0
  85. package/dist/internal/sharingPublic/session/httpClient.js.map +1 -0
  86. package/dist/internal/sharingPublic/session/index.d.ts +1 -0
  87. package/dist/internal/sharingPublic/session/index.js +6 -0
  88. package/dist/internal/sharingPublic/session/index.js.map +1 -0
  89. package/dist/internal/sharingPublic/session/interface.d.ts +18 -0
  90. package/dist/internal/sharingPublic/session/interface.js +3 -0
  91. package/dist/internal/sharingPublic/session/interface.js.map +1 -0
  92. package/dist/internal/sharingPublic/session/manager.d.ts +49 -0
  93. package/dist/internal/sharingPublic/session/manager.js +75 -0
  94. package/dist/internal/sharingPublic/session/manager.js.map +1 -0
  95. package/dist/internal/sharingPublic/session/session.d.ts +34 -0
  96. package/dist/internal/sharingPublic/session/session.js +67 -0
  97. package/dist/internal/sharingPublic/session/session.js.map +1 -0
  98. package/dist/internal/sharingPublic/session/url.d.ts +12 -0
  99. package/dist/internal/sharingPublic/session/url.js +23 -0
  100. package/dist/internal/sharingPublic/session/url.js.map +1 -0
  101. package/dist/internal/sharingPublic/session/url.test.d.ts +1 -0
  102. package/dist/internal/sharingPublic/session/url.test.js +59 -0
  103. package/dist/internal/sharingPublic/session/url.test.js.map +1 -0
  104. package/dist/internal/upload/manager.js +1 -3
  105. package/dist/internal/upload/manager.js.map +1 -1
  106. package/dist/internal/upload/manager.test.js +2 -2
  107. package/dist/internal/upload/manager.test.js.map +1 -1
  108. package/dist/protonDriveClient.d.ts +25 -10
  109. package/dist/protonDriveClient.js +44 -22
  110. package/dist/protonDriveClient.js.map +1 -1
  111. package/dist/protonDrivePublicLinkClient.d.ts +48 -0
  112. package/dist/protonDrivePublicLinkClient.js +71 -0
  113. package/dist/protonDrivePublicLinkClient.js.map +1 -0
  114. package/package.json +1 -1
  115. package/src/crypto/interface.ts +11 -0
  116. package/src/diagnostic/httpClient.ts +4 -4
  117. package/src/diagnostic/interface.ts +27 -29
  118. package/src/diagnostic/sdkDiagnostic.ts +58 -30
  119. package/src/errors.ts +5 -5
  120. package/src/interface/author.ts +1 -1
  121. package/src/interface/events.ts +1 -7
  122. package/src/interface/httpClient.ts +5 -5
  123. package/src/interface/index.ts +18 -6
  124. package/src/internal/apiService/apiService.ts +13 -4
  125. package/src/internal/apiService/errorCodes.ts +1 -0
  126. package/src/internal/apiService/errors.test.ts +2 -1
  127. package/src/internal/apiService/errors.ts +15 -4
  128. package/src/internal/events/index.ts +1 -1
  129. package/src/internal/nodes/apiService.test.ts +28 -0
  130. package/src/internal/nodes/apiService.ts +3 -0
  131. package/src/internal/nodes/cryptoCache.test.ts +4 -7
  132. package/src/internal/nodes/cryptoCache.ts +6 -7
  133. package/src/internal/nodes/cryptoService.ts +68 -34
  134. package/src/internal/nodes/interface.ts +2 -0
  135. package/src/internal/nodes/nodesAccess.ts +2 -2
  136. package/src/internal/nodes/nodesManagement.ts +0 -3
  137. package/src/internal/shares/cryptoCache.test.ts +3 -2
  138. package/src/internal/shares/cryptoCache.ts +26 -7
  139. package/src/internal/shares/index.ts +1 -1
  140. package/src/internal/sharing/cryptoService.test.ts +22 -1
  141. package/src/internal/sharing/cryptoService.ts +8 -6
  142. package/src/internal/sharing/index.ts +1 -0
  143. package/src/internal/sharing/interface.ts +0 -4
  144. package/src/internal/sharing/sharingAccess.test.ts +4 -4
  145. package/src/internal/sharing/sharingAccess.ts +6 -0
  146. package/src/internal/sharing/sharingManagement.test.ts +87 -24
  147. package/src/internal/sharing/sharingManagement.ts +56 -16
  148. package/src/internal/sharingPublic/apiService.ts +164 -0
  149. package/src/internal/sharingPublic/cryptoCache.ts +79 -0
  150. package/src/internal/sharingPublic/cryptoService.ts +162 -0
  151. package/src/internal/sharingPublic/index.ts +40 -0
  152. package/src/internal/sharingPublic/interface.ts +59 -0
  153. package/src/internal/sharingPublic/manager.ts +85 -0
  154. package/src/internal/sharingPublic/session/apiService.ts +74 -0
  155. package/src/internal/sharingPublic/session/httpClient.ts +48 -0
  156. package/src/internal/sharingPublic/session/index.ts +1 -0
  157. package/src/internal/sharingPublic/session/interface.ts +20 -0
  158. package/src/internal/sharingPublic/session/manager.ts +97 -0
  159. package/src/internal/sharingPublic/session/session.ts +78 -0
  160. package/src/internal/sharingPublic/session/url.test.ts +72 -0
  161. package/src/internal/sharingPublic/session/url.ts +23 -0
  162. package/src/internal/upload/manager.test.ts +2 -2
  163. package/src/internal/upload/manager.ts +2 -4
  164. package/src/protonDriveClient.ts +64 -27
  165. package/src/protonDrivePublicLinkClient.ts +121 -0
@@ -0,0 +1,78 @@
1
+ import { SRPModule } from "../../../crypto";
2
+ import { SharingPublicSessionAPIService } from "./apiService";
3
+ import { PublicLinkInfo, PublicLinkSrpInfo } from "./interface";
4
+
5
+ /**
6
+ * Session for a public link.
7
+ *
8
+ * It is responsible for initializing and authenticating the public link session
9
+ * with the SRP handshake. It also can re-authenticate the session if it is expired.
10
+ */
11
+ export class SharingPublicLinkSession {
12
+ private sessionUid?: string;
13
+ private sessionAccessToken?: string;
14
+
15
+ constructor(
16
+ private apiService: SharingPublicSessionAPIService,
17
+ private srpModule: SRPModule,
18
+ private token: string,
19
+ private password: string,
20
+ ) {
21
+ this.apiService = apiService;
22
+ this.srpModule = srpModule;
23
+ this.token = token;
24
+ this.password = password;
25
+ }
26
+
27
+ async reauth(): Promise<void> {
28
+ const info = await this.init();
29
+ await this.auth(info.srp);
30
+ }
31
+
32
+ async init(): Promise<PublicLinkInfo> {
33
+ return this.apiService.initPublicLinkSession(this.token);
34
+ }
35
+
36
+ async auth(srp: PublicLinkSrpInfo): Promise<void> {
37
+ const { expectedServerProof, clientProof, clientEphemeral } = await this.srpModule.getSrp(
38
+ srp.version,
39
+ srp.modulus,
40
+ srp.serverEphemeral,
41
+ srp.salt,
42
+ this.password,
43
+ );
44
+
45
+ const auth = await this.apiService.authPublicLinkSession(this.token, {
46
+ clientProof,
47
+ clientEphemeral,
48
+ srpSession: srp.srpSession,
49
+ });
50
+
51
+ if (auth.serverProof !== expectedServerProof) {
52
+ throw new Error('Invalid server proof');
53
+ }
54
+
55
+ this.sessionUid = auth.sessionUid;
56
+ this.sessionAccessToken = auth.sessionAccessToken;
57
+ }
58
+
59
+ /**
60
+ * Get the session uid and access token.
61
+ *
62
+ * The access token is only returned if the session is newly created.
63
+ * If the access token is not available, it means the existing session
64
+ * can be used to access the public link.
65
+ *
66
+ * @throws If the session is not initialized.
67
+ */
68
+ get session() {
69
+ if (!this.sessionUid) {
70
+ throw new Error('Session not initialized');
71
+ }
72
+
73
+ return {
74
+ uid: this.sessionUid,
75
+ accessToken: this.sessionAccessToken,
76
+ };
77
+ }
78
+ }
@@ -0,0 +1,72 @@
1
+ import { ValidationError } from '../../../errors';
2
+ import { getTokenAndPasswordFromUrl } from './url';
3
+
4
+ describe('getTokenAndPasswordFromUrl', () => {
5
+ describe('valid URLs', () => {
6
+ it('should extract token and password from a valid URL', () => {
7
+ const url = 'https://drive.proton.me/urls/abc123#def456';
8
+ const result = getTokenAndPasswordFromUrl(url);
9
+
10
+ expect(result).toEqual({
11
+ token: 'abc123',
12
+ password: 'def456'
13
+ });
14
+ });
15
+
16
+ it('should handle URLs with different domains', () => {
17
+ const url = 'https://example.com/urls/mytoken#mypassword';
18
+ const result = getTokenAndPasswordFromUrl(url);
19
+
20
+ expect(result).toEqual({
21
+ token: 'mytoken',
22
+ password: 'mypassword'
23
+ });
24
+ });
25
+
26
+ it('should handle URLs with query parameters', () => {
27
+ const url = 'https://drive.proton.me/urls/token123?param=value#password456';
28
+ const result = getTokenAndPasswordFromUrl(url);
29
+
30
+ expect(result).toEqual({
31
+ token: 'token123',
32
+ password: 'password456'
33
+ });
34
+ });
35
+ });
36
+
37
+ describe('should throw ValidationError', () => {
38
+ it('when token is missing (no path)', () => {
39
+ const url = 'https://drive.proton.me/#password123';
40
+
41
+ expect(() => getTokenAndPasswordFromUrl(url)).toThrow(ValidationError);
42
+ });
43
+
44
+ it('when token is missing (empty path segment)', () => {
45
+ const url = 'https://drive.proton.me/urls/#password123';
46
+
47
+ expect(() => getTokenAndPasswordFromUrl(url)).toThrow(ValidationError);
48
+ });
49
+
50
+ it('when password is missing (no hash)', () => {
51
+ const url = 'https://drive.proton.me/urls/token123';
52
+
53
+ expect(() => getTokenAndPasswordFromUrl(url)).toThrow(ValidationError);
54
+ expect(() => getTokenAndPasswordFromUrl(url)).toThrow('Invalid URL');
55
+ });
56
+
57
+ it('when password is empty (empty hash)', () => {
58
+ const url = 'https://drive.proton.me/urls/token123#';
59
+
60
+ expect(() => getTokenAndPasswordFromUrl(url)).toThrow(ValidationError);
61
+ expect(() => getTokenAndPasswordFromUrl(url)).toThrow('Invalid URL');
62
+ });
63
+
64
+ it('for empty string', () => {
65
+ expect(() => getTokenAndPasswordFromUrl('')).toThrow();
66
+ });
67
+
68
+ it('for invalid URL format', () => {
69
+ expect(() => getTokenAndPasswordFromUrl('not-a-url')).toThrow();
70
+ });
71
+ });
72
+ });
@@ -0,0 +1,23 @@
1
+ import { c } from 'ttag';
2
+
3
+ import { ValidationError } from '../../../errors';
4
+
5
+ /**
6
+ * Parse the token and password from the URL.
7
+ *
8
+ * The URL format is: https://drive.proton.me/urls/token#password
9
+ *
10
+ * @param url - The URL of the public link.
11
+ * @returns The token and password.
12
+ */
13
+ export function getTokenAndPasswordFromUrl(url: string): { token: string; password: string } {
14
+ const urlObj = new URL(url);
15
+ const token = urlObj.pathname.split('/').pop();
16
+ const password = urlObj.hash.slice(1);
17
+
18
+ if (!token || !password) {
19
+ throw new ValidationError(c('Error').t`Invalid URL`);
20
+ }
21
+
22
+ return { token, password };
23
+ }
@@ -206,7 +206,7 @@ describe('UploadManager', () => {
206
206
  await promise;
207
207
  } catch (error: any) {
208
208
  expect(error.message).toBe('Draft already exists');
209
- expect(error.ongoingUploadByOtherClient).toBe(true);
209
+ expect(error.isUnfinishedUpload).toBe(true);
210
210
  }
211
211
  expect(apiService.deleteDraft).not.toHaveBeenCalled();
212
212
  });
@@ -237,7 +237,7 @@ describe('UploadManager', () => {
237
237
  await promise;
238
238
  } catch (error: any) {
239
239
  expect(error.message).toBe('Draft already exists');
240
- expect(error.ongoingUploadByOtherClient).toBe(true);
240
+ expect(error.isUnfinishedUpload).toBe(true);
241
241
  }
242
242
  expect(apiService.deleteDraft).not.toHaveBeenCalled();
243
243
  });
@@ -1,7 +1,7 @@
1
1
  import { c } from 'ttag';
2
2
 
3
3
  import { Logger, ProtonDriveTelemetry, UploadMetadata } from '../../interface';
4
- import { ValidationError, NodeAlreadyExistsValidationError } from '../../errors';
4
+ import { ValidationError, NodeWithSameNameExistsValidationError } from '../../errors';
5
5
  import { ErrorCode } from '../apiService';
6
6
  import { generateFileExtendedAttributes } from '../nodes';
7
7
  import { UploadAPIService } from './apiService';
@@ -158,9 +158,7 @@ export class UploadManager {
158
158
  ? makeNodeUid(splitNodeUid(parentFolderUid).volumeId, typedDetails.ConflictLinkID)
159
159
  : undefined;
160
160
 
161
- // If there is existing node, return special error
162
- // that includes the available name the client can use.
163
- throw new NodeAlreadyExistsValidationError(
161
+ throw new NodeWithSameNameExistsValidationError(
164
162
  error.message,
165
163
  error.code,
166
164
  existingNodeUid,
@@ -1,3 +1,5 @@
1
+ import { getConfig } from './config';
2
+ import { DriveCrypto, SessionKey } from './crypto';
1
3
  import {
2
4
  Logger,
3
5
  ProtonDriveClientContructorParameters,
@@ -6,6 +8,7 @@ import {
6
8
  MaybeMissingNode,
7
9
  NodeResult,
8
10
  Revision,
11
+ RevisionOrUid,
9
12
  ShareNodeSettings,
10
13
  UnshareNodeSettings,
11
14
  ProtonInvitationOrUid,
@@ -25,16 +28,6 @@ import {
25
28
  ThumbnailResult,
26
29
  SDKEvent,
27
30
  } from './interface';
28
- import { DriveCrypto, SessionKey } from './crypto';
29
- import { DriveAPIService } from './internal/apiService';
30
- import { initSharesModule } from './internal/shares';
31
- import { initNodesModule } from './internal/nodes';
32
- import { initSharingModule } from './internal/sharing';
33
- import { initDownloadModule } from './internal/download';
34
- import { initUploadModule } from './internal/upload';
35
- import { DriveEventsService, DriveListener } from './internal/events';
36
- import { SDKEvents } from './internal/sdkEvents';
37
- import { getConfig } from './config';
38
31
  import {
39
32
  getUid,
40
33
  getUids,
@@ -44,9 +37,18 @@ import {
44
37
  convertInternalNode,
45
38
  } from './transformers';
46
39
  import { Telemetry } from './telemetry';
40
+ import { DriveAPIService } from './internal/apiService';
47
41
  import { initDevicesModule } from './internal/devices';
42
+ import { initDownloadModule } from './internal/download';
43
+ import { DriveEventsService, DriveListener, EventSubscription } from './internal/events';
44
+ import { initNodesModule } from './internal/nodes';
45
+ import { SDKEvents } from './internal/sdkEvents';
46
+ import { initSharesModule } from './internal/shares';
47
+ import { initSharingModule } from './internal/sharing';
48
+ import { SharingPublicSessionManager } from './internal/sharingPublic';
49
+ import { initUploadModule } from './internal/upload';
48
50
  import { makeNodeUid } from './internal/uids';
49
- import { EventSubscription } from './internal/events/interface';
51
+ import { ProtonDrivePublicLinkClient } from './protonDrivePublicLinkClient';
50
52
 
51
53
  /**
52
54
  * ProtonDriveClient is the main interface for the ProtonDrive SDK.
@@ -65,6 +67,7 @@ export class ProtonDriveClient {
65
67
  private download: ReturnType<typeof initDownloadModule>;
66
68
  private upload: ReturnType<typeof initUploadModule>;
67
69
  private devices: ReturnType<typeof initDevicesModule>;
70
+ private sessionManager: SharingPublicSessionManager;
68
71
 
69
72
  public experimental: {
70
73
  /**
@@ -81,6 +84,20 @@ export class ProtonDriveClient {
81
84
  * This is used by Docs app to encrypt and decrypt document updates.
82
85
  */
83
86
  getDocsKey: (nodeUid: NodeOrUid) => Promise<SessionKey>;
87
+ /**
88
+ * Experimental feature to get the info for a public link
89
+ * required to authenticate the public link.
90
+ */
91
+ getPublicLinkInfo: (url: string) => Promise<{
92
+ isCustomPasswordProtected: boolean;
93
+ isLegacy: boolean;
94
+ vendorType: number;
95
+ }>;
96
+ /**
97
+ * Experimental feature to authenticate a public link and
98
+ * return the client for the public link to access it.
99
+ */
100
+ authPublicLink: (url: string, customPassword?: string) => Promise<ProtonDrivePublicLinkClient>;
84
101
  };
85
102
 
86
103
  constructor({
@@ -166,6 +183,8 @@ export class ProtonDriveClient {
166
183
  latestEventIdProvider,
167
184
  );
168
185
 
186
+ this.sessionManager = new SharingPublicSessionManager(httpClient, apiService, srpModule);
187
+
169
188
  this.experimental = {
170
189
  getNodeUrl: async (nodeUid: NodeOrUid) => {
171
190
  this.logger.debug(`Getting node URL for ${getUid(nodeUid)}`);
@@ -179,6 +198,24 @@ export class ProtonDriveClient {
179
198
  }
180
199
  return keys.contentKeyPacketSessionKey;
181
200
  },
201
+ getPublicLinkInfo: async (url: string) => {
202
+ this.logger.info(`Getting info for public link ${url}`);
203
+ return this.sessionManager.getInfo(url);
204
+ },
205
+ authPublicLink: async (url: string, customPassword?: string) => {
206
+ this.logger.info(`Authenticating public link ${url}`);
207
+ const { httpClient, token, password } = await this.sessionManager.auth(url, customPassword);
208
+ return new ProtonDrivePublicLinkClient({
209
+ httpClient,
210
+ cryptoCache,
211
+ openPGPCryptoModule,
212
+ srpModule,
213
+ config,
214
+ telemetry,
215
+ token,
216
+ password,
217
+ });
218
+ },
182
219
  };
183
220
  }
184
221
 
@@ -311,7 +348,7 @@ export class ProtonDriveClient {
311
348
  * @param nodeUid - Node entity or its UID string.
312
349
  * @returns The updated node entity.
313
350
  * @throws {@link ValidationError} If the name is empty, too long, or contains a slash.
314
- * @throws {@link Error} If another node with the same name already exists.
351
+ * @throws {@link ValidationError} If another node with the same name already exists.
315
352
  */
316
353
  async renameNode(nodeUid: NodeOrUid, newName: string): Promise<MaybeNode> {
317
354
  this.logger.info(`Renaming node ${getUid(nodeUid)}`);
@@ -342,7 +379,7 @@ export class ProtonDriveClient {
342
379
  newParentNodeUid: NodeOrUid,
343
380
  signal?: AbortSignal,
344
381
  ): AsyncGenerator<NodeResult> {
345
- this.logger.info(`Moving ${nodeUids.length} nodes to ${newParentNodeUid}`);
382
+ this.logger.info(`Moving ${nodeUids.length} nodes to ${getUid(newParentNodeUid)}`);
346
383
  yield* this.nodes.management.moveNodes(getUids(nodeUids), getUid(newParentNodeUid), signal);
347
384
  }
348
385
 
@@ -453,9 +490,9 @@ export class ProtonDriveClient {
453
490
  *
454
491
  * @param revisionUid - UID of the revision to restore.
455
492
  */
456
- async restoreRevision(revisionUid: string): Promise<void> {
457
- this.logger.info(`Restoring revision ${revisionUid}`);
458
- await this.nodes.revisions.restoreRevision(revisionUid);
493
+ async restoreRevision(revisionUid: RevisionOrUid): Promise<void> {
494
+ this.logger.info(`Restoring revision ${getUid(revisionUid)}`);
495
+ await this.nodes.revisions.restoreRevision(getUid(revisionUid));
459
496
  }
460
497
 
461
498
  /**
@@ -463,9 +500,9 @@ export class ProtonDriveClient {
463
500
  *
464
501
  * @param revisionUid - UID of the revision to delete.
465
502
  */
466
- async deleteRevision(revisionUid: string): Promise<void> {
467
- this.logger.info(`Deleting revision ${revisionUid}`);
468
- await this.nodes.revisions.deleteRevision(revisionUid);
503
+ async deleteRevision(revisionUid: RevisionOrUid): Promise<void> {
504
+ this.logger.info(`Deleting revision ${getUid(revisionUid)}`);
505
+ await this.nodes.revisions.deleteRevision(getUid(revisionUid));
469
506
  }
470
507
 
471
508
  /**
@@ -527,21 +564,21 @@ export class ProtonDriveClient {
527
564
  /**
528
565
  * Accept the invitation to the shared node.
529
566
  *
530
- * @param invitationId - Invitation entity or its UID string.
567
+ * @param invitationUid - Invitation entity or its UID string.
531
568
  */
532
- async acceptInvitation(invitationId: string): Promise<void> {
533
- this.logger.info(`Accepting invitation ${invitationId}`);
534
- await this.sharing.access.acceptInvitation(invitationId);
569
+ async acceptInvitation(invitationUid: ProtonInvitationOrUid): Promise<void> {
570
+ this.logger.info(`Accepting invitation ${getUid(invitationUid)}`);
571
+ await this.sharing.access.acceptInvitation(getUid(invitationUid));
535
572
  }
536
573
 
537
574
  /**
538
575
  * Reject the invitation to the shared node.
539
576
  *
540
- * @param invitationId - Invitation entity or its UID string.
577
+ * @param invitationOrUid - Invitation entity or its UID string.
541
578
  */
542
- async rejectInvitation(invitationId: string): Promise<void> {
543
- this.logger.info(`Rejecting invitation ${invitationId}`);
544
- await this.sharing.access.rejectInvitation(invitationId);
579
+ async rejectInvitation(invitationUid: ProtonInvitationOrUid): Promise<void> {
580
+ this.logger.info(`Rejecting invitation ${getUid(invitationUid)}`);
581
+ await this.sharing.access.rejectInvitation(getUid(invitationUid));
545
582
  }
546
583
 
547
584
  /**
@@ -0,0 +1,121 @@
1
+ import { getConfig } from './config';
2
+ import { DriveCrypto, OpenPGPCrypto, SRPModule, SessionKey } from './crypto';
3
+ import {
4
+ ProtonDriveHTTPClient,
5
+ ProtonDriveTelemetry,
6
+ ProtonDriveConfig,
7
+ Logger,
8
+ ProtonDriveCryptoCache,
9
+ NodeOrUid,
10
+ } from './interface';
11
+ import { Telemetry } from './telemetry';
12
+ import { getUid } from './transformers';
13
+ import { DriveAPIService } from './internal/apiService';
14
+ import { SDKEvents } from './internal/sdkEvents';
15
+ import { initSharingPublicModule } from './internal/sharingPublic';
16
+
17
+ /**
18
+ * ProtonDrivePublicLinkClient is the interface for the public link client.
19
+ *
20
+ * The client provides high-level operations for managing nodes, and
21
+ * downloading/uploading files.
22
+ *
23
+ * Do not use this client direclty, use ProtonDriveClient instead.
24
+ * The main client handles public link sessions and provides access to
25
+ * public links.
26
+ *
27
+ * See `experimental.getPublicLinkInfo` and `experimental.authPublicLink`
28
+ * for more information.
29
+ */
30
+ export class ProtonDrivePublicLinkClient {
31
+ private logger: Logger;
32
+ private sdkEvents: SDKEvents;
33
+ private sharingPublic: ReturnType<typeof initSharingPublicModule>;
34
+
35
+ public experimental: {
36
+ /**
37
+ * Experimental feature to return the URL of the node.
38
+ *
39
+ * Use it when you want to open the node in the ProtonDrive web app.
40
+ *
41
+ * It has hardcoded URLs to open in production client only.
42
+ */
43
+ getNodeUrl: (nodeUid: NodeOrUid) => Promise<string>;
44
+ /**
45
+ * Experimental feature to get the docs key for a node.
46
+ *
47
+ * This is used by Docs app to encrypt and decrypt document updates.
48
+ */
49
+ getDocsKey: (nodeUid: NodeOrUid) => Promise<SessionKey>;
50
+ };
51
+
52
+ constructor({
53
+ httpClient,
54
+ cryptoCache,
55
+ openPGPCryptoModule,
56
+ srpModule,
57
+ config,
58
+ telemetry,
59
+ token,
60
+ password,
61
+ }: {
62
+ httpClient: ProtonDriveHTTPClient;
63
+ cryptoCache: ProtonDriveCryptoCache;
64
+ openPGPCryptoModule: OpenPGPCrypto;
65
+ srpModule: SRPModule;
66
+ config?: ProtonDriveConfig;
67
+ telemetry?: ProtonDriveTelemetry;
68
+ token: string;
69
+ password: string;
70
+ }) {
71
+ if (!telemetry) {
72
+ telemetry = new Telemetry();
73
+ }
74
+ this.logger = telemetry.getLogger('interface');
75
+
76
+ const fullConfig = getConfig(config);
77
+ this.sdkEvents = new SDKEvents(telemetry);
78
+
79
+ const apiService = new DriveAPIService(
80
+ telemetry,
81
+ this.sdkEvents,
82
+ httpClient,
83
+ fullConfig.baseUrl,
84
+ fullConfig.language,
85
+ );
86
+ const driveCrypto = new DriveCrypto(openPGPCryptoModule, srpModule);
87
+ this.sharingPublic = initSharingPublicModule(telemetry, apiService, cryptoCache, driveCrypto, token, password);
88
+
89
+ this.experimental = {
90
+ getNodeUrl: async (nodeUid: NodeOrUid) => {
91
+ this.logger.debug(`Getting node URL for ${getUid(nodeUid)}`);
92
+ // TODO: public node has different URL
93
+ return '';
94
+ },
95
+ getDocsKey: async (nodeUid: NodeOrUid) => {
96
+ this.logger.debug(`Getting docs keys for ${getUid(nodeUid)}`);
97
+ const keys = await this.sharingPublic.getNodeKeys(getUid(nodeUid));
98
+ if (!keys.contentKeyPacketSessionKey) {
99
+ throw new Error('Node does not have a content key packet session key');
100
+ }
101
+ return keys.contentKeyPacketSessionKey;
102
+ },
103
+ };
104
+ }
105
+
106
+ // TODO: comment
107
+ // TODO: add public node interface
108
+ async getRootNode() {
109
+ this.logger.info(`Getting root node`);
110
+ // TODO: conversion to public node
111
+ return this.sharingPublic.getRootNode();
112
+ }
113
+
114
+ // TODO: comment
115
+ // TODO: add public node interface
116
+ async *iterateChildren(parentUid: NodeOrUid) {
117
+ this.logger.info(`Iterating children of ${getUid(parentUid)}`);
118
+ // TODO: conversion to public node
119
+ yield * this.sharingPublic.iterateChildren(getUid(parentUid));
120
+ }
121
+ }