@protontech/drive-sdk 0.4.1 → 0.5.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 (130) hide show
  1. package/dist/diagnostic/sdkDiagnostic.js +1 -1
  2. package/dist/diagnostic/sdkDiagnostic.js.map +1 -1
  3. package/dist/interface/download.d.ts +4 -4
  4. package/dist/interface/upload.d.ts +6 -3
  5. package/dist/internal/apiService/apiService.d.ts +3 -0
  6. package/dist/internal/apiService/apiService.js +25 -2
  7. package/dist/internal/apiService/apiService.js.map +1 -1
  8. package/dist/internal/apiService/apiService.test.js +38 -0
  9. package/dist/internal/apiService/apiService.test.js.map +1 -1
  10. package/dist/internal/apiService/driveTypes.d.ts +31 -48
  11. package/dist/internal/apiService/errors.js +3 -0
  12. package/dist/internal/apiService/errors.js.map +1 -1
  13. package/dist/internal/apiService/errors.test.js +15 -7
  14. package/dist/internal/apiService/errors.test.js.map +1 -1
  15. package/dist/internal/asyncIteratorMap.d.ts +1 -1
  16. package/dist/internal/asyncIteratorMap.js +6 -1
  17. package/dist/internal/asyncIteratorMap.js.map +1 -1
  18. package/dist/internal/asyncIteratorMap.test.js +9 -0
  19. package/dist/internal/asyncIteratorMap.test.js.map +1 -1
  20. package/dist/internal/download/fileDownloader.d.ts +3 -3
  21. package/dist/internal/download/fileDownloader.js +5 -5
  22. package/dist/internal/download/fileDownloader.js.map +1 -1
  23. package/dist/internal/download/fileDownloader.test.js +8 -8
  24. package/dist/internal/download/fileDownloader.test.js.map +1 -1
  25. package/dist/internal/nodes/apiService.d.ts +6 -1
  26. package/dist/internal/nodes/apiService.js +44 -32
  27. package/dist/internal/nodes/apiService.js.map +1 -1
  28. package/dist/internal/nodes/apiService.test.js +148 -17
  29. package/dist/internal/nodes/apiService.test.js.map +1 -1
  30. package/dist/internal/nodes/debouncer.d.ts +23 -0
  31. package/dist/internal/nodes/debouncer.js +80 -0
  32. package/dist/internal/nodes/debouncer.js.map +1 -0
  33. package/dist/internal/nodes/debouncer.test.d.ts +1 -0
  34. package/dist/internal/nodes/debouncer.test.js +100 -0
  35. package/dist/internal/nodes/debouncer.test.js.map +1 -0
  36. package/dist/internal/nodes/nodesAccess.d.ts +2 -1
  37. package/dist/internal/nodes/nodesAccess.js +24 -5
  38. package/dist/internal/nodes/nodesAccess.js.map +1 -1
  39. package/dist/internal/nodes/nodesAccess.test.js +2 -2
  40. package/dist/internal/nodes/nodesAccess.test.js.map +1 -1
  41. package/dist/internal/photos/upload.d.ts +2 -1
  42. package/dist/internal/photos/upload.js +3 -3
  43. package/dist/internal/photos/upload.js.map +1 -1
  44. package/dist/internal/sharingPublic/apiService.d.ts +2 -2
  45. package/dist/internal/sharingPublic/apiService.js +1 -63
  46. package/dist/internal/sharingPublic/apiService.js.map +1 -1
  47. package/dist/internal/sharingPublic/cryptoCache.d.ts +0 -4
  48. package/dist/internal/sharingPublic/cryptoCache.js +0 -28
  49. package/dist/internal/sharingPublic/cryptoCache.js.map +1 -1
  50. package/dist/internal/sharingPublic/cryptoReporter.d.ts +16 -0
  51. package/dist/internal/sharingPublic/cryptoReporter.js +44 -0
  52. package/dist/internal/sharingPublic/cryptoReporter.js.map +1 -0
  53. package/dist/internal/sharingPublic/cryptoService.d.ts +3 -4
  54. package/dist/internal/sharingPublic/cryptoService.js +5 -43
  55. package/dist/internal/sharingPublic/cryptoService.js.map +1 -1
  56. package/dist/internal/sharingPublic/index.d.ts +21 -3
  57. package/dist/internal/sharingPublic/index.js +43 -12
  58. package/dist/internal/sharingPublic/index.js.map +1 -1
  59. package/dist/internal/sharingPublic/interface.d.ts +0 -1
  60. package/dist/internal/sharingPublic/nodes.d.ts +13 -0
  61. package/dist/internal/sharingPublic/nodes.js +28 -0
  62. package/dist/internal/sharingPublic/nodes.js.map +1 -0
  63. package/dist/internal/sharingPublic/session/session.d.ts +3 -3
  64. package/dist/internal/sharingPublic/session/url.test.js +3 -3
  65. package/dist/internal/sharingPublic/shares.d.ts +34 -0
  66. package/dist/internal/sharingPublic/shares.js +69 -0
  67. package/dist/internal/sharingPublic/shares.js.map +1 -0
  68. package/dist/internal/upload/apiService.js +10 -1
  69. package/dist/internal/upload/apiService.js.map +1 -1
  70. package/dist/internal/upload/controller.d.ts +8 -2
  71. package/dist/internal/upload/controller.js.map +1 -1
  72. package/dist/internal/upload/fileUploader.d.ts +6 -3
  73. package/dist/internal/upload/fileUploader.js +3 -3
  74. package/dist/internal/upload/fileUploader.js.map +1 -1
  75. package/dist/internal/upload/fileUploader.test.js +23 -11
  76. package/dist/internal/upload/fileUploader.test.js.map +1 -1
  77. package/dist/internal/upload/streamUploader.d.ts +6 -2
  78. package/dist/internal/upload/streamUploader.js +8 -4
  79. package/dist/internal/upload/streamUploader.js.map +1 -1
  80. package/dist/internal/upload/streamUploader.test.js +10 -6
  81. package/dist/internal/upload/streamUploader.test.js.map +1 -1
  82. package/dist/protonDriveClient.d.ts +3 -3
  83. package/dist/protonDriveClient.js +4 -4
  84. package/dist/protonDriveClient.js.map +1 -1
  85. package/dist/protonDrivePublicLinkClient.d.ts +31 -4
  86. package/dist/protonDrivePublicLinkClient.js +52 -9
  87. package/dist/protonDrivePublicLinkClient.js.map +1 -1
  88. package/package.json +1 -1
  89. package/src/diagnostic/sdkDiagnostic.ts +1 -1
  90. package/src/interface/download.ts +4 -4
  91. package/src/interface/upload.ts +3 -3
  92. package/src/internal/apiService/apiService.test.ts +50 -0
  93. package/src/internal/apiService/apiService.ts +33 -2
  94. package/src/internal/apiService/driveTypes.ts +31 -48
  95. package/src/internal/apiService/errors.test.ts +10 -0
  96. package/src/internal/apiService/errors.ts +5 -1
  97. package/src/internal/asyncIteratorMap.test.ts +12 -0
  98. package/src/internal/asyncIteratorMap.ts +8 -0
  99. package/src/internal/download/fileDownloader.test.ts +8 -8
  100. package/src/internal/download/fileDownloader.ts +5 -5
  101. package/src/internal/nodes/apiService.test.ts +199 -16
  102. package/src/internal/nodes/apiService.ts +62 -49
  103. package/src/internal/nodes/debouncer.test.ts +129 -0
  104. package/src/internal/nodes/debouncer.ts +93 -0
  105. package/src/internal/nodes/nodesAccess.test.ts +2 -2
  106. package/src/internal/nodes/nodesAccess.ts +30 -5
  107. package/src/internal/photos/upload.ts +4 -1
  108. package/src/internal/sharingPublic/apiService.ts +4 -87
  109. package/src/internal/sharingPublic/cryptoCache.ts +0 -34
  110. package/src/internal/sharingPublic/cryptoReporter.ts +73 -0
  111. package/src/internal/sharingPublic/cryptoService.ts +4 -80
  112. package/src/internal/sharingPublic/index.ts +68 -6
  113. package/src/internal/sharingPublic/interface.ts +0 -9
  114. package/src/internal/sharingPublic/nodes.ts +37 -0
  115. package/src/internal/sharingPublic/session/apiService.ts +1 -1
  116. package/src/internal/sharingPublic/session/session.ts +3 -3
  117. package/src/internal/sharingPublic/session/url.test.ts +3 -3
  118. package/src/internal/sharingPublic/shares.ts +86 -0
  119. package/src/internal/upload/apiService.ts +12 -1
  120. package/src/internal/upload/controller.ts +2 -2
  121. package/src/internal/upload/fileUploader.test.ts +25 -11
  122. package/src/internal/upload/fileUploader.ts +4 -3
  123. package/src/internal/upload/streamUploader.test.ts +15 -3
  124. package/src/internal/upload/streamUploader.ts +8 -3
  125. package/src/protonDriveClient.ts +4 -4
  126. package/src/protonDrivePublicLinkClient.ts +93 -12
  127. package/dist/internal/sharingPublic/manager.d.ts +0 -19
  128. package/dist/internal/sharingPublic/manager.js +0 -81
  129. package/dist/internal/sharingPublic/manager.js.map +0 -1
  130. package/src/internal/sharingPublic/manager.ts +0 -86
@@ -1,12 +1,3 @@
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';
9
-
10
1
  export interface EncryptedShareCrypto {
11
2
  base64UrlPasswordSalt: string;
12
3
  armoredKey: string;
@@ -0,0 +1,37 @@
1
+ import { Logger } from "../../interface";
2
+ import { NodeAPIService } from "../nodes/apiService";
3
+ import { NodesCache } from "../nodes/cache";
4
+ import { NodesCryptoCache } from "../nodes/cryptoCache";
5
+ import { NodesCryptoService } from "../nodes/cryptoService";
6
+ import { NodesAccess } from "../nodes/nodesAccess";
7
+ import { isProtonDocument, isProtonSheet } from "../nodes/mediaTypes";
8
+ import { splitNodeUid } from "../uids";
9
+ import { SharingPublicSharesManager } from "./shares";
10
+
11
+ export class SharingPublicNodesAccess extends NodesAccess {
12
+ constructor(
13
+ logger: Logger,
14
+ apiService: NodeAPIService,
15
+ cache: NodesCache,
16
+ cryptoCache: NodesCryptoCache,
17
+ cryptoService: NodesCryptoService,
18
+ sharesService: SharingPublicSharesManager,
19
+ private url: string,
20
+ private token: string,
21
+ ) {
22
+ super(logger, apiService, cache, cryptoCache, cryptoService, sharesService);
23
+ this.token = token;
24
+ }
25
+
26
+ async getNodeUrl(nodeUid: string): Promise<string> {
27
+ const node = await this.getNode(nodeUid);
28
+ if (isProtonDocument(node.mediaType) || isProtonSheet(node.mediaType)) {
29
+ const { nodeId } = splitNodeUid(nodeUid);
30
+ const type = isProtonDocument(node.mediaType) ? 'doc' : 'sheet';
31
+ return `https://docs.proton.me/doc?type=${type}&mode=open-url&token=${this.token}&linkId=${nodeId}`;
32
+ }
33
+
34
+ // Public link doesn't support specific node URLs.
35
+ return this.url;
36
+ }
37
+ }
@@ -9,7 +9,7 @@ type PostPublicLinkAuthRequest = Extract<
9
9
  { content: object }
10
10
  >['content']['application/json'];
11
11
  type PostPublicLinkAuthResponse =
12
- drivePaths['/drive/urls/{token}/auth']['post']['responses']['200']['content']['application/json'];
12
+ drivePaths['/drive/urls/{token}/auth']['post']['responses']['200']['content']['application/json'];
13
13
 
14
14
  /**
15
15
  * Provides API communication for managing public link session (not data).
@@ -1,6 +1,6 @@
1
- import { SRPModule } from "../../../crypto";
2
- import { SharingPublicSessionAPIService } from "./apiService";
3
- import { PublicLinkInfo, PublicLinkSrpInfo } from "./interface";
1
+ import { SRPModule } from '../../../crypto';
2
+ import { SharingPublicSessionAPIService } from './apiService';
3
+ import { PublicLinkInfo, PublicLinkSrpInfo } from './interface';
4
4
 
5
5
  /**
6
6
  * Session for a public link.
@@ -9,7 +9,7 @@ describe('getTokenAndPasswordFromUrl', () => {
9
9
 
10
10
  expect(result).toEqual({
11
11
  token: 'abc123',
12
- password: 'def456'
12
+ password: 'def456',
13
13
  });
14
14
  });
15
15
 
@@ -19,7 +19,7 @@ describe('getTokenAndPasswordFromUrl', () => {
19
19
 
20
20
  expect(result).toEqual({
21
21
  token: 'mytoken',
22
- password: 'mypassword'
22
+ password: 'mypassword',
23
23
  });
24
24
  });
25
25
 
@@ -29,7 +29,7 @@ describe('getTokenAndPasswordFromUrl', () => {
29
29
 
30
30
  expect(result).toEqual({
31
31
  token: 'token123',
32
- password: 'password456'
32
+ password: 'password456',
33
33
  });
34
34
  });
35
35
  });
@@ -0,0 +1,86 @@
1
+ import { PrivateKey } from '../../crypto';
2
+ import { MetricVolumeType, ProtonDriveAccount } from '../../interface';
3
+ import { splitNodeUid } from '../uids';
4
+ import { SharingPublicAPIService } from './apiService';
5
+ import { SharingPublicCryptoCache } from './cryptoCache';
6
+ import { SharingPublicCryptoService } from './cryptoService';
7
+
8
+ /**
9
+ * Provides high-level actions for managing public link share.
10
+ *
11
+ * The public link share manager provides the same interface as the code share
12
+ * service so it can be used in the same way in various modules that use shares.
13
+ */
14
+ export class SharingPublicSharesManager {
15
+ private promisePublicLinkRoot?: Promise<{
16
+ rootIds: { volumeId: string; rootNodeId: string; rootNodeUid: string };
17
+ shareKey: PrivateKey;
18
+ }>;
19
+
20
+ constructor(
21
+ private apiService: SharingPublicAPIService,
22
+ private cryptoCache: SharingPublicCryptoCache,
23
+ private cryptoService: SharingPublicCryptoService,
24
+ private account: ProtonDriveAccount,
25
+ private token: string,
26
+ ) {
27
+ this.apiService = apiService;
28
+ this.cryptoCache = cryptoCache;
29
+ this.cryptoService = cryptoService;
30
+ this.account = account;
31
+ this.token = token;
32
+ }
33
+
34
+ // TODO: Rename to getRootIDs everywhere.
35
+ async getOwnVolumeIDs(): Promise<{ volumeId: string; rootNodeId: string; rootNodeUid: string }> {
36
+ const { rootIds } = await this.getPublicLinkRoot();
37
+ return rootIds;
38
+ }
39
+
40
+ async getSharePrivateKey(): Promise<PrivateKey> {
41
+ const { shareKey } = await this.getPublicLinkRoot();
42
+ return shareKey;
43
+ }
44
+
45
+ private async getPublicLinkRoot(): Promise<{
46
+ rootIds: { volumeId: string; rootNodeId: string; rootNodeUid: string };
47
+ shareKey: PrivateKey;
48
+ }> {
49
+ if (!this.promisePublicLinkRoot) {
50
+ this.promisePublicLinkRoot = (async () => {
51
+ const { encryptedNode, encryptedShare } = await this.apiService.getPublicLinkRoot(this.token);
52
+
53
+ const { volumeId, nodeId: rootNodeId } = splitNodeUid(encryptedNode.uid);
54
+
55
+ const shareKey = await this.cryptoService.decryptPublicLinkShareKey(encryptedShare);
56
+ await this.cryptoCache.setShareKey(shareKey);
57
+
58
+ return {
59
+ rootIds: { volumeId, rootNodeId, rootNodeUid: encryptedNode.uid },
60
+ shareKey,
61
+ };
62
+ })();
63
+ }
64
+
65
+ return this.promisePublicLinkRoot;
66
+ }
67
+
68
+ async getContextShareMemberEmailKey(): Promise<{
69
+ email: string;
70
+ addressId: string;
71
+ addressKey: PrivateKey;
72
+ addressKeyId: string;
73
+ }> {
74
+ const address = await this.account.getOwnPrimaryAddress();
75
+ return {
76
+ email: address.email,
77
+ addressId: address.addressId,
78
+ addressKey: address.keys[address.primaryKeyIndex].key,
79
+ addressKeyId: address.keys[address.primaryKeyIndex].id,
80
+ };
81
+ }
82
+
83
+ async getVolumeMetricContext(): Promise<MetricVolumeType> {
84
+ return MetricVolumeType.SharedPublic;
85
+ }
86
+ }
@@ -110,6 +110,17 @@ export class UploadAPIService {
110
110
  nodeUid: string;
111
111
  nodeRevisionUid: string;
112
112
  }> {
113
+ // The client shouldn't send the clear text size of the file.
114
+ // The intented upload size is needed only for early validation that
115
+ // the file can fit in the remaining quota to avoid data transfer when
116
+ // the upload would be rejected. The backend will still validate
117
+ // the quota during block upload and revision commit.
118
+ const precision = 100_000; // bytes
119
+ const intendedUploadSize =
120
+ node.intendedUploadSize && node.intendedUploadSize > precision
121
+ ? Math.floor(node.intendedUploadSize / precision) * precision
122
+ : null;
123
+
113
124
  const { volumeId, nodeId: parentNodeId } = splitNodeUid(parentNodeUid);
114
125
  const result = await this.apiService.post<PostCreateDraftRequest, PostCreateDraftResponse>(
115
126
  `drive/v2/volumes/${volumeId}/files`,
@@ -119,7 +130,7 @@ export class UploadAPIService {
119
130
  Hash: node.hash,
120
131
  MIMEType: node.mediaType,
121
132
  ClientUID: this.clientUid || null,
122
- IntendedUploadSize: node.intendedUploadSize || null,
133
+ IntendedUploadSize: intendedUploadSize,
123
134
  NodeKey: node.armoredNodeKey,
124
135
  NodePassphrase: node.armoredNodePassphrase,
125
136
  NodePassphraseSignature: node.armoredNodePassphraseSignature,
@@ -2,7 +2,7 @@ import { waitForCondition } from '../wait';
2
2
 
3
3
  export class UploadController {
4
4
  private paused = false;
5
- public promise?: Promise<string>;
5
+ public promise?: Promise<{ nodeRevisionUid: string, nodeUid: string }>;
6
6
 
7
7
  async waitIfPaused(): Promise<void> {
8
8
  await waitForCondition(() => !this.paused);
@@ -16,7 +16,7 @@ export class UploadController {
16
16
  this.paused = false;
17
17
  }
18
18
 
19
- async completion(): Promise<string> {
19
+ async completion(): Promise<{ nodeRevisionUid: string, nodeUid: string }> {
20
20
  if (!this.promise) {
21
21
  throw new Error('UploadController.completion() called before upload started');
22
22
  }
@@ -108,6 +108,7 @@ describe('FileUploader', () => {
108
108
 
109
109
  revisionDraft = {
110
110
  nodeRevisionUid: 'revisionUid',
111
+ nodeUid: 'nodeUid',
111
112
  nodeKeys: {
112
113
  signatureAddress: { addressId: 'addressId' },
113
114
  },
@@ -131,10 +132,13 @@ describe('FileUploader', () => {
131
132
  abortController.signal,
132
133
  );
133
134
 
134
- startUploadSpy = jest.spyOn(uploader as any, 'startUpload').mockReturnValue(Promise.resolve('revisionUid'));
135
+ startUploadSpy = jest.spyOn(uploader as any, 'startUpload').mockReturnValue(Promise.resolve({
136
+ nodeRevisionUid: 'revisionUid',
137
+ nodeUid: 'nodeUid'
138
+ }));
135
139
  });
136
140
 
137
- describe('writeFile', () => {
141
+ describe('uploadFromFile', () => {
138
142
  // @ts-expect-error Ignore mocking File
139
143
  const file = {
140
144
  type: 'image/png',
@@ -146,50 +150,60 @@ describe('FileUploader', () => {
146
150
  const onProgress = jest.fn();
147
151
 
148
152
  it('should set media type if not set', async () => {
149
- await uploader.writeFile(file, thumbnails, onProgress);
153
+ await uploader.uploadFromFile(file, thumbnails, onProgress);
150
154
 
151
155
  expect(metadata.mediaType).toEqual('image/png');
152
156
  expect(startUploadSpy).toHaveBeenCalledWith('stream', thumbnails, onProgress);
153
157
  });
154
158
 
155
159
  it('should set expected size if not set', async () => {
156
- await uploader.writeFile(file, thumbnails, onProgress);
160
+ await uploader.uploadFromFile(file, thumbnails, onProgress);
157
161
 
158
162
  expect(metadata.expectedSize).toEqual(file.size);
159
163
  expect(startUploadSpy).toHaveBeenCalledWith('stream', thumbnails, onProgress);
160
164
  });
161
165
 
162
166
  it('should set modification time if not set', async () => {
163
- await uploader.writeFile(file, thumbnails, onProgress);
167
+ await uploader.uploadFromFile(file, thumbnails, onProgress);
164
168
 
165
169
  expect(metadata.modificationTime).toEqual(new Date(123456789));
166
170
  expect(startUploadSpy).toHaveBeenCalledWith('stream', thumbnails, onProgress);
167
171
  });
168
172
 
169
173
  it('should throw an error if upload already started', async () => {
170
- await uploader.writeFile(file, thumbnails, onProgress);
174
+ await uploader.uploadFromFile(file, thumbnails, onProgress);
171
175
 
172
- await expect(uploader.writeFile(file, thumbnails, onProgress)).rejects.toThrow('Upload already started');
176
+ await expect(uploader.uploadFromFile(file, thumbnails, onProgress)).rejects.toThrow('Upload already started');
173
177
  });
174
178
  });
175
179
 
176
- describe('writeStream', () => {
180
+ describe('uploadFromStream', () => {
177
181
  const stream = new ReadableStream();
178
182
  const thumbnails: Thumbnail[] = [];
179
183
  const onProgress = jest.fn();
180
184
 
181
185
  it('should start the upload process', async () => {
182
- await uploader.writeStream(stream, thumbnails, onProgress);
186
+ await uploader.uploadFromStream(stream, thumbnails, onProgress);
183
187
 
184
188
  expect(startUploadSpy).toHaveBeenCalledWith(stream, thumbnails, onProgress);
185
189
  });
186
190
 
187
191
  it('should throw an error if upload already started', async () => {
188
- await uploader.writeStream(stream, thumbnails, onProgress);
192
+ await uploader.uploadFromStream(stream, thumbnails, onProgress);
189
193
 
190
- await expect(uploader.writeStream(stream, thumbnails, onProgress)).rejects.toThrow(
194
+ await expect(uploader.uploadFromStream(stream, thumbnails, onProgress)).rejects.toThrow(
191
195
  'Upload already started',
192
196
  );
193
197
  });
198
+
199
+ it('should return correct nodeUid and nodeRevisionUid via controller completion', async () => {
200
+ const controller = await uploader.uploadFromStream(stream, thumbnails, onProgress);
201
+ const result = await controller.completion();
202
+
203
+ expect(result).toEqual({
204
+ nodeRevisionUid: 'revisionUid',
205
+ nodeUid: 'nodeUid'
206
+ });
207
+ });
194
208
  });
195
209
  });
@@ -46,7 +46,7 @@ class Uploader {
46
46
  this.controller = new UploadController();
47
47
  }
48
48
 
49
- async writeFile(
49
+ async uploadFromFile(
50
50
  fileObject: File,
51
51
  thumbnails: Thumbnail[],
52
52
  onProgress?: (uploadedBytes: number) => void,
@@ -67,7 +67,7 @@ class Uploader {
67
67
  return this.controller;
68
68
  }
69
69
 
70
- async writeStream(
70
+ async uploadFromStream(
71
71
  stream: ReadableStream,
72
72
  thumbnails: Thumbnail[],
73
73
  onProgress?: (uploadedBytes: number) => void,
@@ -83,7 +83,7 @@ class Uploader {
83
83
  stream: ReadableStream,
84
84
  thumbnails: Thumbnail[],
85
85
  onProgress?: (uploadedBytes: number) => void,
86
- ): Promise<string> {
86
+ ): Promise<{ nodeRevisionUid: string, nodeUid: string }> {
87
87
  const uploader = await this.initStreamUploader();
88
88
  return uploader.start(stream, thumbnails, onProgress);
89
89
  }
@@ -119,6 +119,7 @@ class Uploader {
119
119
  revisionDraft,
120
120
  this.metadata,
121
121
  onFinish,
122
+ this.controller,
122
123
  this.signal,
123
124
  );
124
125
  }
@@ -108,6 +108,7 @@ describe('StreamUploader', () => {
108
108
 
109
109
  revisionDraft = {
110
110
  nodeRevisionUid: 'revisionUid',
111
+ nodeUid: 'nodeUid',
111
112
  nodeKeys: {
112
113
  signatureAddress: { addressId: 'addressId' },
113
114
  },
@@ -131,6 +132,7 @@ describe('StreamUploader', () => {
131
132
  revisionDraft,
132
133
  metadata,
133
134
  onFinish,
135
+ controller,
134
136
  abortController.signal,
135
137
  );
136
138
  });
@@ -143,7 +145,12 @@ describe('StreamUploader', () => {
143
145
  let stream: ReadableStream<Uint8Array>;
144
146
 
145
147
  const verifySuccess = async () => {
146
- await uploader.start(stream, thumbnails, onProgress);
148
+ const result = await uploader.start(stream, thumbnails, onProgress);
149
+
150
+ expect(result).toEqual({
151
+ nodeRevisionUid: 'revisionUid',
152
+ nodeUid: 'nodeUid'
153
+ });
147
154
 
148
155
  const numberOfExpectedBlocks = Math.ceil(metadata.expectedSize / FILE_CHUNK_SIZE);
149
156
  expect(uploadManager.commitDraft).toHaveBeenCalledTimes(1);
@@ -251,6 +258,8 @@ describe('StreamUploader', () => {
251
258
  revisionDraft,
252
259
  metadata,
253
260
  onFinish,
261
+ controller,
262
+ abortController.signal,
254
263
  );
255
264
 
256
265
  await verifySuccess();
@@ -278,6 +287,8 @@ describe('StreamUploader', () => {
278
287
  revisionDraft,
279
288
  metadata,
280
289
  onFinish,
290
+ controller,
291
+ abortController.signal,
281
292
  );
282
293
 
283
294
  await verifySuccess();
@@ -459,9 +470,10 @@ describe('StreamUploader', () => {
459
470
  {
460
471
  // Fake expected size to break verification
461
472
  expectedSize: 1 * 1024 * 1024 + 1024,
462
- mediaType: '',
463
- },
473
+ } as UploadMetadata,
464
474
  onFinish,
475
+ controller,
476
+ abortController.signal,
465
477
  );
466
478
 
467
479
  await verifyFailure(
@@ -84,6 +84,7 @@ export class StreamUploader {
84
84
  protected revisionDraft: NodeRevisionDraft,
85
85
  protected metadata: UploadMetadata,
86
86
  protected onFinish: (failure: boolean) => Promise<void>,
87
+ protected uploadController: UploadController,
87
88
  protected signal?: AbortSignal,
88
89
  ) {
89
90
  this.telemetry = telemetry;
@@ -104,14 +105,14 @@ export class StreamUploader {
104
105
  }
105
106
 
106
107
  this.digests = new UploadDigests();
107
- this.controller = new UploadController();
108
+ this.controller = uploadController;
108
109
  }
109
110
 
110
111
  async start(
111
112
  stream: ReadableStream,
112
113
  thumbnails: Thumbnail[],
113
114
  onProgress?: (uploadedBytes: number) => void,
114
- ): Promise<string> {
115
+ ): Promise<{ nodeRevisionUid: string, nodeUid: string }> {
115
116
  let failure = false;
116
117
 
117
118
  // File progress is tracked for telemetry - to track at what
@@ -154,7 +155,11 @@ export class StreamUploader {
154
155
  await this.onFinish(failure);
155
156
  }
156
157
 
157
- return this.revisionDraft.nodeRevisionUid;
158
+ return {
159
+ nodeRevisionUid: this.revisionDraft.nodeRevisionUid,
160
+ nodeUid: this.revisionDraft.nodeUid
161
+ }
162
+
158
163
  }
159
164
 
160
165
  private async encryptAndUploadBlocks(
@@ -208,12 +208,12 @@ export class ProtonDriveClient {
208
208
  const { httpClient, token, password } = await this.sessionManager.auth(url, customPassword);
209
209
  return new ProtonDrivePublicLinkClient({
210
210
  httpClient,
211
- cryptoCache,
212
211
  account,
213
212
  openPGPCryptoModule,
214
213
  srpModule,
215
214
  config,
216
215
  telemetry,
216
+ url,
217
217
  token,
218
218
  password,
219
219
  });
@@ -726,7 +726,7 @@ export class ProtonDriveClient {
726
726
  * ```typescript
727
727
  * const downloader = await client.getFileDownloader(nodeUid, signal);
728
728
  * const claimedSize = fileDownloader.getClaimedSizeInBytes();
729
- * const downloadController = fileDownloader.writeToStream(stream, (downloadedBytes) => { ... });
729
+ * const downloadController = fileDownloader.downloadToStream(stream, (downloadedBytes) => { ... });
730
730
  *
731
731
  * signalController.abort(); // to cancel
732
732
  * downloadController.pause(); // to pause
@@ -786,12 +786,12 @@ export class ProtonDriveClient {
786
786
  *
787
787
  * ```typescript
788
788
  * const uploader = await client.getFileUploader(parentFolderUid, name, metadata, signal);
789
- * const uploadController = await uploader.writeStream(stream, thumbnails, (uploadedBytes) => { ... });
789
+ * const uploadController = await uploader.uploadFromStream(stream, thumbnails, (uploadedBytes) => { ... });
790
790
  *
791
791
  * signalController.abort(); // to cancel
792
792
  * uploadController.pause(); // to pause
793
793
  * uploadController.resume(); // to resume
794
- * const nodeUid = await uploadController.completion(); // to await completion
794
+ * const { nodeUid, nodeRevisionUid } = await uploadController.completion(); // to await completion
795
795
  * ```
796
796
  */
797
797
  async getFileUploader(