@protontech/drive-sdk 0.4.1 → 0.5.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 (197) hide show
  1. package/dist/diagnostic/{sdkDiagnosticFull.d.ts → diagnostic.d.ts} +5 -4
  2. package/dist/diagnostic/{sdkDiagnosticFull.js → diagnostic.js} +13 -10
  3. package/dist/diagnostic/diagnostic.js.map +1 -0
  4. package/dist/diagnostic/index.js +2 -4
  5. package/dist/diagnostic/index.js.map +1 -1
  6. package/dist/diagnostic/interface.d.ts +22 -1
  7. package/dist/diagnostic/sdkDiagnostic.d.ts +3 -2
  8. package/dist/diagnostic/sdkDiagnostic.js +80 -8
  9. package/dist/diagnostic/sdkDiagnostic.js.map +1 -1
  10. package/dist/interface/download.d.ts +4 -4
  11. package/dist/interface/index.d.ts +1 -1
  12. package/dist/interface/index.js.map +1 -1
  13. package/dist/interface/nodes.d.ts +9 -0
  14. package/dist/interface/telemetry.d.ts +4 -1
  15. package/dist/interface/telemetry.js.map +1 -1
  16. package/dist/interface/upload.d.ts +6 -3
  17. package/dist/internal/apiService/apiService.d.ts +3 -0
  18. package/dist/internal/apiService/apiService.js +25 -2
  19. package/dist/internal/apiService/apiService.js.map +1 -1
  20. package/dist/internal/apiService/apiService.test.js +38 -0
  21. package/dist/internal/apiService/apiService.test.js.map +1 -1
  22. package/dist/internal/apiService/driveTypes.d.ts +2595 -2397
  23. package/dist/internal/apiService/errors.js +3 -0
  24. package/dist/internal/apiService/errors.js.map +1 -1
  25. package/dist/internal/apiService/errors.test.js +15 -7
  26. package/dist/internal/apiService/errors.test.js.map +1 -1
  27. package/dist/internal/asyncIteratorMap.d.ts +1 -1
  28. package/dist/internal/asyncIteratorMap.js +6 -1
  29. package/dist/internal/asyncIteratorMap.js.map +1 -1
  30. package/dist/internal/asyncIteratorMap.test.js +9 -0
  31. package/dist/internal/asyncIteratorMap.test.js.map +1 -1
  32. package/dist/internal/download/controller.d.ts +2 -0
  33. package/dist/internal/download/controller.js +15 -1
  34. package/dist/internal/download/controller.js.map +1 -1
  35. package/dist/internal/download/fileDownloader.d.ts +3 -3
  36. package/dist/internal/download/fileDownloader.js +11 -6
  37. package/dist/internal/download/fileDownloader.js.map +1 -1
  38. package/dist/internal/download/fileDownloader.test.js +8 -8
  39. package/dist/internal/download/fileDownloader.test.js.map +1 -1
  40. package/dist/internal/nodes/apiService.d.ts +6 -1
  41. package/dist/internal/nodes/apiService.js +71 -44
  42. package/dist/internal/nodes/apiService.js.map +1 -1
  43. package/dist/internal/nodes/apiService.test.js +204 -15
  44. package/dist/internal/nodes/apiService.test.js.map +1 -1
  45. package/dist/internal/nodes/debouncer.d.ts +24 -0
  46. package/dist/internal/nodes/debouncer.js +92 -0
  47. package/dist/internal/nodes/debouncer.js.map +1 -0
  48. package/dist/internal/nodes/debouncer.test.d.ts +1 -0
  49. package/dist/internal/nodes/debouncer.test.js +108 -0
  50. package/dist/internal/nodes/debouncer.test.js.map +1 -0
  51. package/dist/internal/nodes/extendedAttributes.js +2 -2
  52. package/dist/internal/nodes/extendedAttributes.js.map +1 -1
  53. package/dist/internal/nodes/index.js +1 -1
  54. package/dist/internal/nodes/index.js.map +1 -1
  55. package/dist/internal/nodes/nodesAccess.d.ts +6 -4
  56. package/dist/internal/nodes/nodesAccess.js +29 -9
  57. package/dist/internal/nodes/nodesAccess.js.map +1 -1
  58. package/dist/internal/nodes/nodesAccess.test.js +19 -7
  59. package/dist/internal/nodes/nodesAccess.test.js.map +1 -1
  60. package/dist/internal/nodes/nodesManagement.d.ts +2 -2
  61. package/dist/internal/nodes/nodesManagement.js +5 -3
  62. package/dist/internal/nodes/nodesManagement.js.map +1 -1
  63. package/dist/internal/nodes/nodesManagement.test.js +3 -1
  64. package/dist/internal/nodes/nodesManagement.test.js.map +1 -1
  65. package/dist/internal/photos/apiService.js +9 -20
  66. package/dist/internal/photos/apiService.js.map +1 -1
  67. package/dist/internal/photos/upload.d.ts +2 -1
  68. package/dist/internal/photos/upload.js +9 -3
  69. package/dist/internal/photos/upload.js.map +1 -1
  70. package/dist/internal/sharing/apiService.d.ts +1 -1
  71. package/dist/internal/sharing/apiService.js +2 -2
  72. package/dist/internal/sharing/apiService.js.map +1 -1
  73. package/dist/internal/sharing/sharingManagement.d.ts +4 -1
  74. package/dist/internal/sharing/sharingManagement.js +7 -4
  75. package/dist/internal/sharing/sharingManagement.js.map +1 -1
  76. package/dist/internal/sharingPublic/apiService.d.ts +8 -10
  77. package/dist/internal/sharingPublic/apiService.js +9 -125
  78. package/dist/internal/sharingPublic/apiService.js.map +1 -1
  79. package/dist/internal/sharingPublic/cryptoReporter.d.ts +16 -0
  80. package/dist/internal/sharingPublic/{cryptoService.js → cryptoReporter.js} +3 -16
  81. package/dist/internal/sharingPublic/cryptoReporter.js.map +1 -0
  82. package/dist/internal/sharingPublic/index.d.ts +22 -4
  83. package/dist/internal/sharingPublic/index.js +37 -12
  84. package/dist/internal/sharingPublic/index.js.map +1 -1
  85. package/dist/internal/sharingPublic/nodes.d.ts +18 -0
  86. package/dist/internal/sharingPublic/nodes.js +46 -0
  87. package/dist/internal/sharingPublic/nodes.js.map +1 -0
  88. package/dist/internal/sharingPublic/session/apiService.d.ts +7 -5
  89. package/dist/internal/sharingPublic/session/apiService.js +25 -4
  90. package/dist/internal/sharingPublic/session/apiService.js.map +1 -1
  91. package/dist/internal/sharingPublic/session/interface.d.ts +17 -0
  92. package/dist/internal/sharingPublic/session/manager.d.ts +12 -4
  93. package/dist/internal/sharingPublic/session/manager.js +14 -4
  94. package/dist/internal/sharingPublic/session/manager.js.map +1 -1
  95. package/dist/internal/sharingPublic/session/session.d.ts +7 -4
  96. package/dist/internal/sharingPublic/session/session.js +7 -3
  97. package/dist/internal/sharingPublic/session/session.js.map +1 -1
  98. package/dist/internal/sharingPublic/session/url.test.js +3 -3
  99. package/dist/internal/sharingPublic/shares.d.ts +27 -0
  100. package/dist/internal/sharingPublic/shares.js +46 -0
  101. package/dist/internal/sharingPublic/shares.js.map +1 -0
  102. package/dist/internal/upload/apiService.js +10 -1
  103. package/dist/internal/upload/apiService.js.map +1 -1
  104. package/dist/internal/upload/controller.d.ts +11 -3
  105. package/dist/internal/upload/controller.js +16 -2
  106. package/dist/internal/upload/controller.js.map +1 -1
  107. package/dist/internal/upload/fileUploader.d.ts +6 -3
  108. package/dist/internal/upload/fileUploader.js +4 -4
  109. package/dist/internal/upload/fileUploader.js.map +1 -1
  110. package/dist/internal/upload/fileUploader.test.js +23 -11
  111. package/dist/internal/upload/fileUploader.test.js.map +1 -1
  112. package/dist/internal/upload/streamUploader.d.ts +9 -4
  113. package/dist/internal/upload/streamUploader.js +67 -20
  114. package/dist/internal/upload/streamUploader.js.map +1 -1
  115. package/dist/internal/upload/streamUploader.test.js +43 -13
  116. package/dist/internal/upload/streamUploader.test.js.map +1 -1
  117. package/dist/protonDriveClient.d.ts +11 -6
  118. package/dist/protonDriveClient.js +11 -10
  119. package/dist/protonDriveClient.js.map +1 -1
  120. package/dist/protonDrivePublicLinkClient.d.ts +34 -6
  121. package/dist/protonDrivePublicLinkClient.js +52 -9
  122. package/dist/protonDrivePublicLinkClient.js.map +1 -1
  123. package/dist/tests/telemetry.d.ts +4 -2
  124. package/dist/tests/telemetry.js +3 -1
  125. package/dist/tests/telemetry.js.map +1 -1
  126. package/dist/transformers.d.ts +3 -2
  127. package/dist/transformers.js +6 -0
  128. package/dist/transformers.js.map +1 -1
  129. package/package.json +1 -1
  130. package/src/diagnostic/{sdkDiagnosticFull.ts → diagnostic.ts} +10 -6
  131. package/src/diagnostic/index.ts +3 -5
  132. package/src/diagnostic/interface.ts +39 -0
  133. package/src/diagnostic/sdkDiagnostic.ts +111 -10
  134. package/src/interface/download.ts +4 -4
  135. package/src/interface/index.ts +1 -0
  136. package/src/interface/nodes.ts +3 -0
  137. package/src/interface/telemetry.ts +5 -0
  138. package/src/interface/upload.ts +3 -3
  139. package/src/internal/apiService/apiService.test.ts +50 -0
  140. package/src/internal/apiService/apiService.ts +33 -2
  141. package/src/internal/apiService/driveTypes.ts +2713 -2561
  142. package/src/internal/apiService/errors.test.ts +10 -0
  143. package/src/internal/apiService/errors.ts +5 -1
  144. package/src/internal/asyncIteratorMap.test.ts +12 -0
  145. package/src/internal/asyncIteratorMap.ts +8 -0
  146. package/src/internal/download/controller.ts +13 -1
  147. package/src/internal/download/fileDownloader.test.ts +8 -8
  148. package/src/internal/download/fileDownloader.ts +13 -6
  149. package/src/internal/nodes/apiService.test.ts +261 -14
  150. package/src/internal/nodes/apiService.ts +99 -65
  151. package/src/internal/nodes/debouncer.test.ts +141 -0
  152. package/src/internal/nodes/debouncer.ts +109 -0
  153. package/src/internal/nodes/extendedAttributes.ts +2 -2
  154. package/src/internal/nodes/index.ts +1 -8
  155. package/src/internal/nodes/nodesAccess.test.ts +19 -7
  156. package/src/internal/nodes/nodesAccess.ts +44 -9
  157. package/src/internal/nodes/nodesManagement.test.ts +3 -1
  158. package/src/internal/nodes/nodesManagement.ts +11 -5
  159. package/src/internal/photos/apiService.ts +12 -29
  160. package/src/internal/photos/upload.ts +22 -1
  161. package/src/internal/sharing/apiService.ts +2 -2
  162. package/src/internal/sharing/sharingManagement.ts +7 -4
  163. package/src/internal/sharingPublic/apiService.ts +23 -160
  164. package/src/internal/sharingPublic/{cryptoService.ts → cryptoReporter.ts} +2 -27
  165. package/src/internal/sharingPublic/index.ts +76 -13
  166. package/src/internal/sharingPublic/nodes.ts +59 -0
  167. package/src/internal/sharingPublic/session/apiService.ts +32 -10
  168. package/src/internal/sharingPublic/session/interface.ts +20 -0
  169. package/src/internal/sharingPublic/session/manager.ts +31 -8
  170. package/src/internal/sharingPublic/session/session.ts +12 -7
  171. package/src/internal/sharingPublic/session/url.test.ts +3 -3
  172. package/src/internal/sharingPublic/shares.ts +50 -0
  173. package/src/internal/upload/apiService.ts +12 -1
  174. package/src/internal/upload/controller.ts +16 -4
  175. package/src/internal/upload/fileUploader.test.ts +25 -11
  176. package/src/internal/upload/fileUploader.ts +6 -5
  177. package/src/internal/upload/streamUploader.test.ts +56 -12
  178. package/src/internal/upload/streamUploader.ts +78 -20
  179. package/src/protonDriveClient.ts +29 -11
  180. package/src/protonDrivePublicLinkClient.ts +100 -16
  181. package/src/tests/telemetry.ts +6 -3
  182. package/src/transformers.ts +8 -0
  183. package/dist/diagnostic/sdkDiagnosticFull.js.map +0 -1
  184. package/dist/internal/sharingPublic/cryptoCache.d.ts +0 -19
  185. package/dist/internal/sharingPublic/cryptoCache.js +0 -72
  186. package/dist/internal/sharingPublic/cryptoCache.js.map +0 -1
  187. package/dist/internal/sharingPublic/cryptoService.d.ts +0 -9
  188. package/dist/internal/sharingPublic/cryptoService.js.map +0 -1
  189. package/dist/internal/sharingPublic/interface.d.ts +0 -6
  190. package/dist/internal/sharingPublic/interface.js +0 -3
  191. package/dist/internal/sharingPublic/interface.js.map +0 -1
  192. package/dist/internal/sharingPublic/manager.d.ts +0 -19
  193. package/dist/internal/sharingPublic/manager.js +0 -81
  194. package/dist/internal/sharingPublic/manager.js.map +0 -1
  195. package/src/internal/sharingPublic/cryptoCache.ts +0 -79
  196. package/src/internal/sharingPublic/interface.ts +0 -14
  197. package/src/internal/sharingPublic/manager.ts +0 -86
@@ -1,6 +1,6 @@
1
1
  import { Author, FileDownloader, MaybeNode, NodeType, Revision, ThumbnailType } from '../interface';
2
2
  import { ProtonDriveClient } from '../protonDriveClient';
3
- import { Diagnostic, DiagnosticOptions, DiagnosticResult, NodeDetails } from './interface';
3
+ import { DiagnosticOptions, DiagnosticResult, NodeDetails, ExcpectedTreeNode } from './interface';
4
4
  import { IntegrityVerificationStream } from './integrityVerificationStream';
5
5
 
6
6
  /**
@@ -10,7 +10,7 @@ import { IntegrityVerificationStream } from './integrityVerificationStream';
10
10
  * It produces only events that can be read by direct SDK invocation.
11
11
  * To get the full diagnostic, use {@link FullSDKDiagnostic}.
12
12
  */
13
- export class SDKDiagnostic implements Diagnostic {
13
+ export class SDKDiagnostic {
14
14
  constructor(private protonDriveClient: ProtonDriveClient) {
15
15
  this.protonDriveClient = protonDriveClient;
16
16
  }
@@ -58,7 +58,7 @@ export class SDKDiagnostic implements Diagnostic {
58
58
  yield* this.verifyAuthor(activeRevision.contentAuthor, 'content', node);
59
59
  }
60
60
 
61
- yield* this.verifyFileExtendedAttributes(node);
61
+ yield* this.verifyFileExtendedAttributes(node, options);
62
62
 
63
63
  if (options?.verifyContent) {
64
64
  yield* this.verifyContent(node);
@@ -80,12 +80,17 @@ export class SDKDiagnostic implements Diagnostic {
80
80
  }
81
81
  }
82
82
 
83
- private async *verifyFileExtendedAttributes(node: MaybeNode): AsyncGenerator<DiagnosticResult> {
83
+ private async *verifyFileExtendedAttributes(
84
+ node: MaybeNode,
85
+ options?: DiagnosticOptions,
86
+ ): AsyncGenerator<DiagnosticResult> {
84
87
  const activeRevision = getActiveRevision(node);
85
88
 
86
89
  const expectedAttributes = getNodeType(node) === NodeType.File;
87
90
 
88
91
  const claimedSha1 = activeRevision?.claimedDigests?.sha1;
92
+ const claimedSizeInBytes = activeRevision?.claimedSize;
93
+
89
94
  if (claimedSha1 && !/^[0-9a-f]{40}$/i.test(claimedSha1)) {
90
95
  yield {
91
96
  type: 'extended_attributes_error',
@@ -102,6 +107,24 @@ export class SDKDiagnostic implements Diagnostic {
102
107
  ...getNodeDetails(node),
103
108
  };
104
109
  }
110
+
111
+ if (options?.expectedStructure) {
112
+ const expectedSha1 = options.expectedStructure.expectedSha1;
113
+ const expectedSizeInBytes = options.expectedStructure.expectedSizeInBytes;
114
+
115
+ const wrongSha1 = expectedSha1 !== undefined && claimedSha1 !== expectedSha1;
116
+ const wrongSizeInBytes = expectedSizeInBytes !== undefined && claimedSizeInBytes !== expectedSizeInBytes;
117
+
118
+ if (wrongSha1 || wrongSizeInBytes) {
119
+ yield {
120
+ type: 'expected_structure_integrity_error',
121
+ claimedSha1,
122
+ claimedSizeInBytes,
123
+ expectedNode: getExpectedTreeNodeDetails(options.expectedStructure),
124
+ ...getNodeDetails(node),
125
+ };
126
+ }
127
+ }
105
128
  }
106
129
 
107
130
  private async *verifyContent(node: MaybeNode): AsyncGenerator<DiagnosticResult> {
@@ -133,7 +156,7 @@ export class SDKDiagnostic implements Diagnostic {
133
156
  const claimedSizeInBytes = downloader.getClaimedSizeInBytes();
134
157
 
135
158
  const integrityVerificationStream = new IntegrityVerificationStream();
136
- const controller = downloader.writeToStream(integrityVerificationStream);
159
+ const controller = downloader.downloadToStream(integrityVerificationStream);
137
160
 
138
161
  try {
139
162
  await controller.completion();
@@ -195,19 +218,73 @@ export class SDKDiagnostic implements Diagnostic {
195
218
  }
196
219
  }
197
220
 
198
- private async *verifyNodeChildren(node: MaybeNode, options?: DiagnosticOptions): AsyncGenerator<DiagnosticResult> {
199
- const nodeUid = node.ok ? node.value.uid : node.error.uid;
221
+ private async *verifyNodeChildren(
222
+ parentNode: MaybeNode,
223
+ options?: DiagnosticOptions,
224
+ ): AsyncGenerator<DiagnosticResult> {
225
+ const parentNodeUid = parentNode.ok ? parentNode.value.uid : parentNode.error.uid;
226
+ const children: MaybeNode[] = [];
227
+
200
228
  try {
201
- for await (const child of this.protonDriveClient.iterateFolderChildren(node)) {
202
- yield* this.verifyNodeTree(child, options);
229
+ for await (const child of this.protonDriveClient.iterateFolderChildren(parentNode)) {
230
+ if (options?.expectedStructure) {
231
+ children.push(child);
232
+ }
233
+
234
+ yield *
235
+ this.verifyNodeTree(child, {
236
+ ...options,
237
+ expectedStructure: options?.expectedStructure
238
+ ? getTreeNodeChildByNodeName(options.expectedStructure, getNodeName(child))
239
+ : undefined,
240
+ });
203
241
  }
204
242
  } catch (error: unknown) {
205
243
  yield {
206
244
  type: 'sdk_error',
207
- call: `iterateFolderChildren(${nodeUid})`,
245
+ call: `iterateFolderChildren(${parentNodeUid})`,
208
246
  error,
209
247
  };
210
248
  }
249
+
250
+ if (options?.expectedStructure) {
251
+ yield* this.verifyExpectedNodeChildren(parentNodeUid, children, options);
252
+ }
253
+ }
254
+
255
+ private async *verifyExpectedNodeChildren(
256
+ parentNodeUid: string,
257
+ children: MaybeNode[],
258
+ options: DiagnosticOptions,
259
+ ): AsyncGenerator<DiagnosticResult> {
260
+ if (!options.expectedStructure) {
261
+ return;
262
+ }
263
+
264
+ const expectedNodes = options.expectedStructure.children ?? [];
265
+ const actualNodeNames = children.map((child) => getNodeName(child));
266
+
267
+ for (const expectedNode of expectedNodes) {
268
+ if (!actualNodeNames.includes(expectedNode.name)) {
269
+ yield {
270
+ type: 'expected_structure_missing_node',
271
+ expectedNode: getExpectedTreeNodeDetails(expectedNode),
272
+ parentNodeUid,
273
+ };
274
+ }
275
+ }
276
+
277
+ for (const child of children) {
278
+ const childName = getNodeName(child);
279
+ const isExpected = expectedNodes.some((expectedNode) => expectedNode.name === childName);
280
+
281
+ if (!isExpected) {
282
+ yield {
283
+ type: 'expected_structure_unexpected_node',
284
+ ...getNodeDetails(child),
285
+ };
286
+ }
287
+ }
211
288
  }
212
289
  }
213
290
 
@@ -275,3 +352,27 @@ function getActiveRevision(node: MaybeNode): Revision | undefined {
275
352
  }
276
353
  return undefined;
277
354
  }
355
+
356
+ function getNodeName(node: MaybeNode): string {
357
+ if (node.ok) {
358
+ return node.value.name;
359
+ }
360
+ if (node.error.name.ok) {
361
+ return node.error.name.value;
362
+ }
363
+ return 'N/A';
364
+ }
365
+
366
+ function getExpectedTreeNodeDetails(expectedNode: ExcpectedTreeNode): ExcpectedTreeNode {
367
+ return {
368
+ ...expectedNode,
369
+ children: undefined,
370
+ };
371
+ }
372
+
373
+ function getTreeNodeChildByNodeName(
374
+ expectedSubtree: ExcpectedTreeNode,
375
+ nodeName: string,
376
+ ): ExcpectedTreeNode | undefined {
377
+ return expectedSubtree.children?.find((expectedNode) => expectedNode.name === nodeName);
378
+ }
@@ -15,14 +15,14 @@ export interface FileDownloader {
15
15
  *
16
16
  * @param onProgress - Callback that is called with the number of downloaded bytes
17
17
  */
18
- writeToStream(streamFactory: WritableStream, onProgress?: (downloadedBytes: number) => void): DownloadController;
18
+ downloadToStream(streamFactory: WritableStream, onProgress?: (downloadedBytes: number) => void): DownloadController;
19
19
 
20
20
  /**
21
- * Same as `writeToStream` but without verification checks.
21
+ * Same as `downloadToStream` but without verification checks.
22
22
  *
23
23
  * Use this only for debugging purposes.
24
24
  */
25
- unsafeWriteToStream(
25
+ unsafeDownloadToStream(
26
26
  streamFactory: WritableStream,
27
27
  onProgress?: (downloadedBytes: number) => void,
28
28
  ): DownloadController;
@@ -34,7 +34,7 @@ export interface FileDownloader {
34
34
  * need to download the entire file.
35
35
  *
36
36
  * Stream doesn't verify data integrity. For the full integrity of
37
- * the file, use `writeToStream` instead.
37
+ * the file, use `downloadToStream` instead.
38
38
  *
39
39
  * The stream is not opportunitistically downloading the data ahead of
40
40
  * the time. It will only download the data when it is requested. To
@@ -41,6 +41,7 @@ export type {
41
41
  NodeOrUid,
42
42
  RevisionOrUid,
43
43
  NodeResult,
44
+ NodeResultWithNewUid,
44
45
  Membership,
45
46
  } from './nodes';
46
47
  export { NodeType, MemberRole, RevisionState } from './nodes';
@@ -224,3 +224,6 @@ export type NodeOrUid = MaybeNode | NodeEntity | DegradedNode | string;
224
224
  export type RevisionOrUid = Revision | string;
225
225
 
226
226
  export type NodeResult = { uid: string; ok: true } | { uid: string; ok: false; error: string };
227
+ export type NodeResultWithNewUid =
228
+ | { uid: string; newUid: string; ok: true }
229
+ | { uid: string; ok: false; error: string };
@@ -12,6 +12,7 @@ export interface Logger {
12
12
 
13
13
  export type MetricEvent =
14
14
  | MetricAPIRetrySucceededEvent
15
+ | MetricDebounceLongWaitEvent
15
16
  | MetricUploadEvent
16
17
  | MetricDownloadEvent
17
18
  | MetricDecryptionErrorEvent
@@ -25,6 +26,10 @@ export interface MetricAPIRetrySucceededEvent {
25
26
  failedAttempts: number;
26
27
  }
27
28
 
29
+ export interface MetricDebounceLongWaitEvent {
30
+ eventName: 'debounceLongWait';
31
+ }
32
+
28
33
  export interface MetricUploadEvent {
29
34
  eventName: 'upload';
30
35
  volumeType?: MetricVolumeType;
@@ -41,7 +41,7 @@ export interface FileRevisionUploader {
41
41
  *
42
42
  * The function will reject if the node with the given name already exists.
43
43
  */
44
- writeStream(
44
+ uploadFromStream(
45
45
  stream: ReadableStream,
46
46
  thumnbails: Thumbnail[],
47
47
  onProgress?: (uploadedBytes: number) => void,
@@ -57,7 +57,7 @@ export interface FileRevisionUploader {
57
57
  *
58
58
  * The function will reject if the node with the given name already exists.
59
59
  */
60
- writeFile(
60
+ uploadFromFile(
61
61
  fileObject: File,
62
62
  thumnbails: Thumbnail[],
63
63
  onProgress?: (uploadedBytes: number) => void,
@@ -79,5 +79,5 @@ export interface FileUploader extends FileRevisionUploader {
79
79
  export interface UploadController {
80
80
  pause(): void;
81
81
  resume(): void;
82
- completion(): Promise<string>;
82
+ completion(): Promise<{ nodeRevisionUid: string, nodeUid: string }>;
83
83
  }
@@ -1,3 +1,4 @@
1
+ import { AbortError } from '../../errors';
1
2
  import { ProtonDriveHTTPClient, SDKEvent } from '../../interface';
2
3
  import { getMockTelemetry } from '../../tests/telemetry';
3
4
  import { SDKEvents } from '../sdkEvents';
@@ -112,6 +113,16 @@ describe('DriveAPIService', () => {
112
113
  });
113
114
 
114
115
  describe('should throw', () => {
116
+ it('AbortError on aborted error from the provided HTTP client', async () => {
117
+ const abortError = new Error('AbortError');
118
+ abortError.name = 'AbortError';
119
+
120
+ httpClient.fetchJson = jest.fn(() => Promise.reject(abortError));
121
+
122
+ await expect(api.get('test')).rejects.toThrow(new AbortError('Request aborted'));
123
+ expectSDKEvents();
124
+ });
125
+
115
126
  it('APIHTTPError on 4xx response without JSON body', async () => {
116
127
  httpClient.fetchJson = jest.fn(() =>
117
128
  Promise.resolve(new Response('Not found', { status: 404, statusText: 'Not found' })),
@@ -314,5 +325,44 @@ describe('DriveAPIService', () => {
314
325
  expect(httpClient.fetchJson).toHaveBeenCalledTimes(35);
315
326
  expectSDKEvents();
316
327
  });
328
+
329
+ it('notify about offline error', async () => {
330
+ jest.useFakeTimers();
331
+ const offlineError = new Error('OfflineError');
332
+ offlineError.name = 'OfflineError';
333
+
334
+ let attempt = 0;
335
+ httpClient.fetchJson = jest.fn().mockImplementation(() => {
336
+ if (attempt++ >= 15) {
337
+ return generateOkResponse();
338
+ }
339
+ throw offlineError;
340
+ });
341
+
342
+ const promise = api.get('test');
343
+
344
+ // First 9 calls (first is immediate, then 8 with 5 second delay), no events are sent yet
345
+ await jest.advanceTimersByTimeAsync(5 * 8 * 1000);
346
+ expect(httpClient.fetchJson).toHaveBeenCalledTimes(9);
347
+ expectSDKEvents();
348
+
349
+ // 10th call, service sends TransfersPaused event
350
+ await jest.advanceTimersByTimeAsync(5 * 1000);
351
+ expect(httpClient.fetchJson).toHaveBeenCalledTimes(10);
352
+ expectSDKEvents(SDKEvent.TransfersPaused);
353
+
354
+ // Next 5 calls, still offline, no more events are sent
355
+ await jest.advanceTimersByTimeAsync(5 * 5 * 1000);
356
+ expect(httpClient.fetchJson).toHaveBeenCalledTimes(15);
357
+ expectSDKEvents(SDKEvent.TransfersPaused);
358
+
359
+ // 16th call, mock returns OK response, service sends TransfersResumed event
360
+ await jest.advanceTimersByTimeAsync(5 * 1000);
361
+ expect(httpClient.fetchJson).toHaveBeenCalledTimes(16);
362
+ expectSDKEvents(SDKEvent.TransfersPaused, SDKEvent.TransfersResumed);
363
+
364
+ await promise;
365
+ });
366
+
317
367
  });
318
368
  });
@@ -40,6 +40,11 @@ const TOO_MANY_SUBSEQUENT_SERVER_ERRORS = 10;
40
40
  */
41
41
  const TOO_MANY_SUBSEQUENT_SERVER_ERRORS_TIMEOUT_IN_SECONDS = 60;
42
42
 
43
+ /**
44
+ * How many subsequent offline errors are allowed before we consider the client offline.
45
+ */
46
+ const TOO_MANY_SUBSEQUENT_OFFLINE_ERRORS = 10;
47
+
43
48
  /**
44
49
  * After how long to re-try after 5xx or timeout error.
45
50
  */
@@ -88,6 +93,8 @@ export class DriveAPIService {
88
93
  private subsequentServerErrorsCounter = 0;
89
94
  private lastServerErrorAt?: number;
90
95
 
96
+ private subsequentOfflineErrorsCounter = 0;
97
+
91
98
  private logger: Logger;
92
99
 
93
100
  constructor(
@@ -219,7 +226,7 @@ export class DriveAPIService {
219
226
  if (error instanceof ProtonDriveError) {
220
227
  throw error;
221
228
  }
222
- throw apiErrorFactory({ response });
229
+ throw apiErrorFactory({ response, error });
223
230
  }
224
231
  }
225
232
  return response;
@@ -261,7 +268,13 @@ export class DriveAPIService {
261
268
  response = await callback();
262
269
  } catch (error: unknown) {
263
270
  if (error instanceof Error) {
271
+ if (error.name === 'AbortError') {
272
+ this.logger.debug(`${request.method} ${request.url}: Aborted`);
273
+ throw new AbortError(c('Error').t`Request aborted`);
274
+ }
275
+
264
276
  if (error.name === 'OfflineError') {
277
+ this.offlineErrorHappened();
265
278
  this.logger.info(`${request.method} ${request.url}: Offline error, retrying`);
266
279
  await waitSeconds(OFFLINE_RETRY_DELAY_SECONDS);
267
280
  return this.fetch(request, callback, attempt + 1);
@@ -282,6 +295,8 @@ export class DriveAPIService {
282
295
  throw error;
283
296
  }
284
297
 
298
+ this.clearSubsequentOfflineErrors();
299
+
285
300
  const end = Date.now();
286
301
  const duration = end - start;
287
302
 
@@ -342,7 +357,7 @@ export class DriveAPIService {
342
357
  // the client is very limited. This is generic event and it doesn't
343
358
  // take into account that various endpoints can be rate limited
344
359
  // independently.
345
- if (this.subsequentTooManyRequestsCounter >= TOO_MANY_SUBSEQUENT_429_ERRORS) {
360
+ if (this.subsequentTooManyRequestsCounter === TOO_MANY_SUBSEQUENT_429_ERRORS) {
346
361
  this.sdkEvents.requestsThrottled();
347
362
  }
348
363
  }
@@ -373,4 +388,20 @@ export class DriveAPIService {
373
388
  this.subsequentServerErrorsCounter = 0;
374
389
  this.lastServerErrorAt = undefined;
375
390
  }
391
+
392
+ private offlineErrorHappened() {
393
+ this.subsequentOfflineErrorsCounter++;
394
+
395
+ if (this.subsequentOfflineErrorsCounter === TOO_MANY_SUBSEQUENT_OFFLINE_ERRORS) {
396
+ this.sdkEvents.transfersPaused();
397
+ }
398
+ }
399
+
400
+ private clearSubsequentOfflineErrors() {
401
+ if (this.subsequentOfflineErrorsCounter >= TOO_MANY_SUBSEQUENT_OFFLINE_ERRORS) {
402
+ this.sdkEvents.transfersResumed();
403
+ }
404
+
405
+ this.subsequentOfflineErrorsCounter = 0;
406
+ }
376
407
  }