@protontech/drive-sdk 0.0.13 → 0.1.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.
- package/dist/cache/index.d.ts +1 -0
- package/dist/cache/index.js +3 -1
- package/dist/cache/index.js.map +1 -1
- package/dist/cache/memoryCache.d.ts +1 -1
- package/dist/cache/nullCache.d.ts +14 -0
- package/dist/cache/nullCache.js +37 -0
- package/dist/cache/nullCache.js.map +1 -0
- package/dist/config.d.ts +16 -1
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/crypto/openPGPCrypto.js +2 -0
- package/dist/crypto/openPGPCrypto.js.map +1 -1
- package/dist/diagnostic/eventsGenerator.d.ts +14 -0
- package/dist/diagnostic/eventsGenerator.js +49 -0
- package/dist/diagnostic/eventsGenerator.js.map +1 -0
- package/dist/diagnostic/httpClient.d.ts +16 -0
- package/dist/diagnostic/httpClient.js +81 -0
- package/dist/diagnostic/httpClient.js.map +1 -0
- package/dist/diagnostic/index.d.ts +10 -0
- package/dist/diagnostic/index.js +35 -0
- package/dist/diagnostic/index.js.map +1 -0
- package/dist/diagnostic/integrityVerificationStream.d.ts +21 -0
- package/dist/diagnostic/integrityVerificationStream.js +56 -0
- package/dist/diagnostic/integrityVerificationStream.js.map +1 -0
- package/dist/diagnostic/interface.d.ts +102 -0
- package/dist/diagnostic/interface.js +3 -0
- package/dist/diagnostic/interface.js.map +1 -0
- package/dist/diagnostic/sdkDiagnostic.d.ts +22 -0
- package/dist/diagnostic/sdkDiagnostic.js +216 -0
- package/dist/diagnostic/sdkDiagnostic.js.map +1 -0
- package/dist/diagnostic/sdkDiagnosticFull.d.ts +18 -0
- package/dist/diagnostic/sdkDiagnosticFull.js +35 -0
- package/dist/diagnostic/sdkDiagnosticFull.js.map +1 -0
- package/dist/diagnostic/telemetry.d.ts +25 -0
- package/dist/diagnostic/telemetry.js +70 -0
- package/dist/diagnostic/telemetry.js.map +1 -0
- package/dist/diagnostic/zipGenerators.d.ts +9 -0
- package/dist/diagnostic/zipGenerators.js +64 -0
- package/dist/diagnostic/zipGenerators.js.map +1 -0
- package/dist/diagnostic/zipGenerators.test.js +144 -0
- package/dist/diagnostic/zipGenerators.test.js.map +1 -0
- package/dist/errors.d.ts +2 -1
- package/dist/errors.js +3 -1
- package/dist/errors.js.map +1 -1
- package/dist/interface/config.d.ts +26 -0
- package/dist/interface/config.js +3 -0
- package/dist/interface/config.js.map +1 -0
- package/dist/interface/download.d.ts +2 -2
- package/dist/interface/events.d.ts +60 -20
- package/dist/interface/events.js +11 -1
- package/dist/interface/events.js.map +1 -1
- package/dist/interface/httpClient.d.ts +0 -14
- package/dist/interface/index.d.ts +8 -4
- package/dist/interface/index.js +2 -1
- package/dist/interface/index.js.map +1 -1
- package/dist/interface/nodes.d.ts +9 -0
- package/dist/interface/nodes.js.map +1 -1
- package/dist/interface/sharing.d.ts +1 -0
- package/dist/interface/upload.d.ts +6 -0
- package/dist/internal/download/apiService.js +32 -31
- package/dist/internal/download/apiService.js.map +1 -1
- package/dist/internal/download/fileDownloader.d.ts +2 -2
- package/dist/internal/download/fileDownloader.js.map +1 -1
- package/dist/internal/events/apiService.d.ts +4 -6
- package/dist/internal/events/apiService.js +15 -22
- package/dist/internal/events/apiService.js.map +1 -1
- package/dist/internal/events/coreEventManager.d.ts +7 -10
- package/dist/internal/events/coreEventManager.js +19 -36
- package/dist/internal/events/coreEventManager.js.map +1 -1
- package/dist/internal/events/coreEventManager.test.js +87 -0
- package/dist/internal/events/coreEventManager.test.js.map +1 -0
- package/dist/internal/events/eventManager.d.ts +11 -36
- package/dist/internal/events/eventManager.js +59 -105
- package/dist/internal/events/eventManager.js.map +1 -1
- package/dist/internal/events/eventManager.test.js +167 -82
- package/dist/internal/events/eventManager.test.js.map +1 -1
- package/dist/internal/events/index.d.ts +13 -33
- package/dist/internal/events/index.js +56 -72
- package/dist/internal/events/index.js.map +1 -1
- package/dist/internal/events/interface.d.ts +59 -14
- package/dist/internal/events/interface.js +13 -3
- package/dist/internal/events/interface.js.map +1 -1
- package/dist/internal/events/volumeEventManager.d.ts +7 -17
- package/dist/internal/events/volumeEventManager.js +58 -45
- package/dist/internal/events/volumeEventManager.js.map +1 -1
- package/dist/internal/events/volumeEventManager.test.d.ts +1 -0
- package/dist/internal/events/volumeEventManager.test.js +203 -0
- package/dist/internal/events/volumeEventManager.test.js.map +1 -0
- package/dist/internal/nodes/cache.d.ts +10 -1
- package/dist/internal/nodes/cache.js +17 -0
- package/dist/internal/nodes/cache.js.map +1 -1
- package/dist/internal/nodes/cryptoService.d.ts +1 -1
- package/dist/internal/nodes/cryptoService.js.map +1 -1
- package/dist/internal/nodes/events.d.ts +7 -83
- package/dist/internal/nodes/events.js +43 -217
- package/dist/internal/nodes/events.js.map +1 -1
- package/dist/internal/nodes/events.test.js +27 -277
- package/dist/internal/nodes/events.test.js.map +1 -1
- package/dist/internal/nodes/index.d.ts +3 -4
- package/dist/internal/nodes/index.js +5 -5
- package/dist/internal/nodes/index.js.map +1 -1
- package/dist/internal/nodes/nodesAccess.d.ts +15 -0
- package/dist/internal/nodes/nodesAccess.js +37 -0
- package/dist/internal/nodes/nodesAccess.js.map +1 -1
- package/dist/internal/nodes/nodesAccess.test.js +131 -93
- package/dist/internal/nodes/nodesAccess.test.js.map +1 -1
- package/dist/internal/nodes/nodesManagement.d.ts +1 -3
- package/dist/internal/nodes/nodesManagement.js +12 -26
- package/dist/internal/nodes/nodesManagement.js.map +1 -1
- package/dist/internal/nodes/nodesManagement.test.js +35 -14
- package/dist/internal/nodes/nodesManagement.test.js.map +1 -1
- package/dist/internal/shares/cache.d.ts +2 -0
- package/dist/internal/shares/cache.js +2 -0
- package/dist/internal/shares/cache.js.map +1 -1
- package/dist/internal/shares/manager.d.ts +1 -0
- package/dist/internal/shares/manager.js +3 -0
- package/dist/internal/shares/manager.js.map +1 -1
- package/dist/internal/sharing/apiService.js +1 -0
- package/dist/internal/sharing/apiService.js.map +1 -1
- package/dist/internal/sharing/cryptoService.js +1 -0
- package/dist/internal/sharing/cryptoService.js.map +1 -1
- package/dist/internal/sharing/events.d.ts +23 -55
- package/dist/internal/sharing/events.js +46 -138
- package/dist/internal/sharing/events.js.map +1 -1
- package/dist/internal/sharing/events.test.js +77 -180
- package/dist/internal/sharing/events.test.js.map +1 -1
- package/dist/internal/sharing/index.d.ts +4 -5
- package/dist/internal/sharing/index.js +5 -5
- package/dist/internal/sharing/index.js.map +1 -1
- package/dist/internal/sharing/interface.d.ts +3 -0
- package/dist/internal/sharing/sharingManagement.d.ts +2 -3
- package/dist/internal/sharing/sharingManagement.js +7 -9
- package/dist/internal/sharing/sharingManagement.js.map +1 -1
- package/dist/internal/sharing/sharingManagement.test.js +9 -39
- package/dist/internal/sharing/sharingManagement.test.js.map +1 -1
- package/dist/internal/upload/apiService.d.ts +2 -3
- package/dist/internal/upload/apiService.js +7 -4
- package/dist/internal/upload/apiService.js.map +1 -1
- package/dist/internal/upload/index.d.ts +2 -2
- package/dist/internal/upload/index.js +3 -3
- package/dist/internal/upload/index.js.map +1 -1
- package/dist/internal/upload/interface.d.ts +2 -0
- package/dist/internal/upload/manager.d.ts +5 -5
- package/dist/internal/upload/manager.js +19 -50
- package/dist/internal/upload/manager.js.map +1 -1
- package/dist/internal/upload/manager.test.js +68 -44
- package/dist/internal/upload/manager.test.js.map +1 -1
- package/dist/internal/upload/streamUploader.js +1 -2
- package/dist/internal/upload/streamUploader.js.map +1 -1
- package/dist/internal/upload/streamUploader.test.js +1 -1
- package/dist/internal/upload/streamUploader.test.js.map +1 -1
- package/dist/protonDriveClient.d.ts +19 -162
- package/dist/protonDriveClient.js +26 -190
- package/dist/protonDriveClient.js.map +1 -1
- package/dist/protonDrivePhotosClient.js +3 -2
- package/dist/protonDrivePhotosClient.js.map +1 -1
- package/package.json +3 -3
- package/src/cache/index.ts +1 -0
- package/src/cache/memoryCache.ts +1 -1
- package/src/cache/nullCache.ts +38 -0
- package/src/config.ts +17 -2
- package/src/crypto/openPGPCrypto.ts +2 -0
- package/src/diagnostic/eventsGenerator.ts +48 -0
- package/src/diagnostic/httpClient.ts +80 -0
- package/src/diagnostic/index.ts +38 -0
- package/src/diagnostic/integrityVerificationStream.ts +56 -0
- package/src/diagnostic/interface.ts +158 -0
- package/src/diagnostic/sdkDiagnostic.ts +238 -0
- package/src/diagnostic/sdkDiagnosticFull.ts +40 -0
- package/src/diagnostic/telemetry.ts +71 -0
- package/src/diagnostic/zipGenerators.test.ts +177 -0
- package/src/diagnostic/zipGenerators.ts +70 -0
- package/src/errors.ts +4 -1
- package/src/interface/config.ts +28 -0
- package/src/interface/download.ts +2 -2
- package/src/interface/events.ts +66 -21
- package/src/interface/httpClient.ts +0 -16
- package/src/interface/index.ts +8 -4
- package/src/interface/nodes.ts +21 -12
- package/src/interface/sharing.ts +1 -0
- package/src/interface/upload.ts +6 -0
- package/src/internal/download/apiService.ts +11 -8
- package/src/internal/download/fileDownloader.ts +2 -2
- package/src/internal/events/apiService.ts +25 -28
- package/src/internal/events/coreEventManager.test.ts +101 -0
- package/src/internal/events/coreEventManager.ts +20 -45
- package/src/internal/events/eventManager.test.ts +201 -88
- package/src/internal/events/eventManager.ts +69 -115
- package/src/internal/events/index.ts +54 -84
- package/src/internal/events/interface.ts +70 -15
- package/src/internal/events/volumeEventManager.test.ts +243 -0
- package/src/internal/events/volumeEventManager.ts +55 -53
- package/src/internal/nodes/cache.ts +20 -2
- package/src/internal/nodes/cryptoService.ts +1 -1
- package/src/internal/nodes/events.test.ts +29 -335
- package/src/internal/nodes/events.ts +45 -253
- package/src/internal/nodes/index.ts +6 -8
- package/src/internal/nodes/interface.ts +2 -2
- package/src/internal/nodes/nodesAccess.test.ts +132 -91
- package/src/internal/nodes/nodesAccess.ts +40 -1
- package/src/internal/nodes/nodesManagement.test.ts +39 -15
- package/src/internal/nodes/nodesManagement.ts +12 -30
- package/src/internal/shares/cache.ts +4 -2
- package/src/internal/shares/manager.ts +9 -5
- package/src/internal/sharing/apiService.ts +1 -0
- package/src/internal/sharing/cache.ts +1 -1
- package/src/internal/sharing/cryptoService.ts +1 -0
- package/src/internal/sharing/events.test.ts +89 -195
- package/src/internal/sharing/events.ts +42 -156
- package/src/internal/sharing/index.ts +6 -9
- package/src/internal/sharing/interface.ts +6 -2
- package/src/internal/sharing/sharingManagement.test.ts +10 -40
- package/src/internal/sharing/sharingManagement.ts +7 -11
- package/src/internal/upload/apiService.ts +5 -6
- package/src/internal/upload/index.ts +5 -5
- package/src/internal/upload/interface.ts +2 -0
- package/src/internal/upload/manager.test.ts +75 -45
- package/src/internal/upload/manager.ts +24 -54
- package/src/internal/upload/streamUploader.test.ts +0 -1
- package/src/internal/upload/streamUploader.ts +0 -2
- package/src/protonDriveClient.ts +75 -244
- package/src/protonDrivePhotosClient.ts +4 -3
- package/dist/internal/events/cache.d.ts +0 -28
- package/dist/internal/events/cache.js +0 -67
- package/dist/internal/events/cache.js.map +0 -1
- package/dist/internal/events/cache.test.js +0 -43
- package/dist/internal/events/cache.test.js.map +0 -1
- package/dist/internal/nodes/index.test.js +0 -114
- package/dist/internal/nodes/index.test.js.map +0 -1
- package/src/internal/events/cache.test.ts +0 -47
- package/src/internal/events/cache.ts +0 -80
- package/src/internal/nodes/index.test.ts +0 -137
- /package/dist/{internal/events/cache.test.d.ts → diagnostic/zipGenerators.test.d.ts} +0 -0
- /package/dist/internal/{nodes/index.test.d.ts → events/coreEventManager.test.d.ts} +0 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { zipGenerators } from './zipGenerators';
|
|
2
|
+
|
|
3
|
+
async function* createTimedGenerator<T>(values: { value: T; delay: number }[]): AsyncGenerator<T> {
|
|
4
|
+
for (const { value, delay } of values) {
|
|
5
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
6
|
+
yield value;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async function* createEmptyGenerator<T>(): AsyncGenerator<T> {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
describe('zipGenerators', () => {
|
|
15
|
+
it('should handle both generators being empty', async () => {
|
|
16
|
+
const genA = createEmptyGenerator<string>();
|
|
17
|
+
const genB = createEmptyGenerator<number>();
|
|
18
|
+
|
|
19
|
+
const result: (string | number)[] = [];
|
|
20
|
+
const zipGen = zipGenerators(genA, genB);
|
|
21
|
+
|
|
22
|
+
for await (const value of zipGen) {
|
|
23
|
+
result.push(value);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
expect(result).toEqual([]);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should handle one generator being empty (first empty)', async () => {
|
|
30
|
+
const genA = createEmptyGenerator<string>();
|
|
31
|
+
const genB = createTimedGenerator([
|
|
32
|
+
{ value: 1, delay: 10 },
|
|
33
|
+
{ value: 2, delay: 10 },
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
const result: (string | number)[] = [];
|
|
37
|
+
const zipGen = zipGenerators(genA, genB);
|
|
38
|
+
|
|
39
|
+
const promise = (async () => {
|
|
40
|
+
for await (const value of zipGen) {
|
|
41
|
+
result.push(value);
|
|
42
|
+
}
|
|
43
|
+
})();
|
|
44
|
+
|
|
45
|
+
await promise;
|
|
46
|
+
|
|
47
|
+
expect(result).toEqual([1, 2]);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should handle one generator being empty (second empty)', async () => {
|
|
51
|
+
const genA = createTimedGenerator([
|
|
52
|
+
{ value: 'a', delay: 10 },
|
|
53
|
+
{ value: 'b', delay: 10 },
|
|
54
|
+
]);
|
|
55
|
+
const genB = createEmptyGenerator<number>();
|
|
56
|
+
|
|
57
|
+
const result: (string | number)[] = [];
|
|
58
|
+
const zipGen = zipGenerators(genA, genB);
|
|
59
|
+
|
|
60
|
+
const promise = (async () => {
|
|
61
|
+
for await (const value of zipGen) {
|
|
62
|
+
result.push(value);
|
|
63
|
+
}
|
|
64
|
+
})();
|
|
65
|
+
|
|
66
|
+
await promise;
|
|
67
|
+
|
|
68
|
+
expect(result).toEqual(['a', 'b']);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should handle both generators with same number of elements yielded at same time', async () => {
|
|
72
|
+
const genA = createTimedGenerator([
|
|
73
|
+
{ value: 'a1', delay: 10 },
|
|
74
|
+
{ value: 'a2', delay: 10 },
|
|
75
|
+
{ value: 'a3', delay: 10 },
|
|
76
|
+
]);
|
|
77
|
+
const genB = createTimedGenerator([
|
|
78
|
+
{ value: 'b1', delay: 10 },
|
|
79
|
+
{ value: 'b2', delay: 10 },
|
|
80
|
+
{ value: 'b3', delay: 10 },
|
|
81
|
+
]);
|
|
82
|
+
|
|
83
|
+
const result: string[] = [];
|
|
84
|
+
const zipGen = zipGenerators(genA, genB);
|
|
85
|
+
|
|
86
|
+
const promise = (async () => {
|
|
87
|
+
for await (const value of zipGen) {
|
|
88
|
+
result.push(value);
|
|
89
|
+
}
|
|
90
|
+
})();
|
|
91
|
+
|
|
92
|
+
await promise;
|
|
93
|
+
|
|
94
|
+
// Since they yield at the same time, the order depends on Promise.race behavior
|
|
95
|
+
// Both values should be present, but order may vary
|
|
96
|
+
expect(result).toHaveLength(6);
|
|
97
|
+
expect(result).toEqual(expect.arrayContaining(['a1', 'a2', 'a3', 'b1', 'b2', 'b3']));
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should handle generators with different timing - first generator faster', async () => {
|
|
101
|
+
const genA = createTimedGenerator([
|
|
102
|
+
{ value: 'fast1', delay: 10 },
|
|
103
|
+
{ value: 'fast2', delay: 10 },
|
|
104
|
+
{ value: 'fast3', delay: 10 },
|
|
105
|
+
]);
|
|
106
|
+
const genB = createTimedGenerator([
|
|
107
|
+
{ value: 'slow1', delay: 50 },
|
|
108
|
+
{ value: 'slow2', delay: 50 },
|
|
109
|
+
]);
|
|
110
|
+
|
|
111
|
+
const result: string[] = [];
|
|
112
|
+
const zipGen = zipGenerators(genA, genB);
|
|
113
|
+
|
|
114
|
+
const promise = (async () => {
|
|
115
|
+
for await (const value of zipGen) {
|
|
116
|
+
result.push(value);
|
|
117
|
+
}
|
|
118
|
+
})();
|
|
119
|
+
|
|
120
|
+
await promise;
|
|
121
|
+
|
|
122
|
+
expect(result).toEqual(['fast1', 'fast2', 'fast3', 'slow1', 'slow2']);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('should handle generators with different timing - second generator faster', async () => {
|
|
126
|
+
const genA = createTimedGenerator([
|
|
127
|
+
{ value: 'slow1', delay: 50 },
|
|
128
|
+
{ value: 'slow2', delay: 50 },
|
|
129
|
+
]);
|
|
130
|
+
const genB = createTimedGenerator([
|
|
131
|
+
{ value: 'fast1', delay: 10 },
|
|
132
|
+
{ value: 'fast2', delay: 10 },
|
|
133
|
+
{ value: 'fast3', delay: 10 },
|
|
134
|
+
]);
|
|
135
|
+
|
|
136
|
+
const result: string[] = [];
|
|
137
|
+
const zipGen = zipGenerators(genA, genB);
|
|
138
|
+
|
|
139
|
+
const promise = (async () => {
|
|
140
|
+
for await (const value of zipGen) {
|
|
141
|
+
result.push(value);
|
|
142
|
+
}
|
|
143
|
+
})();
|
|
144
|
+
|
|
145
|
+
await promise;
|
|
146
|
+
|
|
147
|
+
expect(result).toEqual(['fast1', 'fast2', 'fast3', 'slow1', 'slow2']);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('should handle mixed timing with overlapping yields', async () => {
|
|
151
|
+
const genA = createTimedGenerator([
|
|
152
|
+
{ value: 'A1', delay: 50 },
|
|
153
|
+
{ value: 'A2', delay: 100 },
|
|
154
|
+
{ value: 'A3', delay: 100 },
|
|
155
|
+
]);
|
|
156
|
+
const genB = createTimedGenerator([
|
|
157
|
+
{ value: 'B1', delay: 100 },
|
|
158
|
+
{ value: 'B2', delay: 100 },
|
|
159
|
+
{ value: 'B3', delay: 200 },
|
|
160
|
+
]);
|
|
161
|
+
|
|
162
|
+
const result: string[] = [];
|
|
163
|
+
const timestamps: number[] = [];
|
|
164
|
+
const zipGen = zipGenerators(genA, genB);
|
|
165
|
+
|
|
166
|
+
const promise = (async () => {
|
|
167
|
+
for await (const value of zipGen) {
|
|
168
|
+
result.push(value);
|
|
169
|
+
timestamps.push(Date.now());
|
|
170
|
+
}
|
|
171
|
+
})();
|
|
172
|
+
|
|
173
|
+
await promise;
|
|
174
|
+
|
|
175
|
+
expect(result).toEqual(['A1', 'B1', 'A2', 'B2', 'A3', 'B3']);
|
|
176
|
+
});
|
|
177
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zips two generators into one.
|
|
3
|
+
*
|
|
4
|
+
* The combined generator yields values from both generators in the order they
|
|
5
|
+
* are produced.
|
|
6
|
+
*/
|
|
7
|
+
export async function* zipGenerators<T, U>(
|
|
8
|
+
genA: AsyncGenerator<T>,
|
|
9
|
+
genB: AsyncGenerator<U>,
|
|
10
|
+
options?: {
|
|
11
|
+
stopOnFirstDone?: boolean
|
|
12
|
+
},
|
|
13
|
+
): AsyncGenerator<T | U> {
|
|
14
|
+
const { stopOnFirstDone = false } = options || {};
|
|
15
|
+
|
|
16
|
+
const itA = genA[Symbol.asyncIterator]();
|
|
17
|
+
const itB = genB[Symbol.asyncIterator]();
|
|
18
|
+
|
|
19
|
+
let promiseA: Promise<IteratorResult<T>> | undefined = itA.next();
|
|
20
|
+
let promiseB: Promise<IteratorResult<U>> | undefined = itB.next();
|
|
21
|
+
|
|
22
|
+
while (promiseA && promiseB) {
|
|
23
|
+
const result = await Promise.race([
|
|
24
|
+
promiseA.then(res => ({ source: 'A' as const, result: res })),
|
|
25
|
+
promiseB.then(res => ({ source: 'B' as const, result: res }))
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
if (result.source === 'A') {
|
|
29
|
+
if (result.result.done) {
|
|
30
|
+
promiseA = undefined;
|
|
31
|
+
if (stopOnFirstDone) {
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
} else {
|
|
35
|
+
yield result.result.value;
|
|
36
|
+
promiseA = itA.next();
|
|
37
|
+
}
|
|
38
|
+
} else {
|
|
39
|
+
if (result.result.done) {
|
|
40
|
+
promiseB = undefined;
|
|
41
|
+
if (stopOnFirstDone) {
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
yield result.result.value;
|
|
46
|
+
promiseB = itB.next();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (stopOnFirstDone) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (promiseA) {
|
|
56
|
+
const result = await promiseA;
|
|
57
|
+
if (!result.done) {
|
|
58
|
+
yield result.value;
|
|
59
|
+
}
|
|
60
|
+
yield* itA;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (promiseB) {
|
|
64
|
+
const result = await promiseB;
|
|
65
|
+
if (!result.done) {
|
|
66
|
+
yield result.value;
|
|
67
|
+
}
|
|
68
|
+
yield* itB;
|
|
69
|
+
}
|
|
70
|
+
}
|
package/src/errors.ts
CHANGED
|
@@ -75,9 +75,12 @@ export class NodeAlreadyExistsValidationError extends ValidationError {
|
|
|
75
75
|
|
|
76
76
|
public readonly existingNodeUid?: string;
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
public readonly ongoingUploadByOtherClient: boolean;
|
|
79
|
+
|
|
80
|
+
constructor(message: string, code: number, existingNodeUid?: string, ongoingUploadByOtherClient = false) {
|
|
79
81
|
super(message, code);
|
|
80
82
|
this.existingNodeUid = existingNodeUid;
|
|
83
|
+
this.ongoingUploadByOtherClient = ongoingUploadByOtherClient;
|
|
81
84
|
}
|
|
82
85
|
}
|
|
83
86
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type ProtonDriveConfig = {
|
|
2
|
+
/**
|
|
3
|
+
* The base URL for the Proton Drive (without schema).
|
|
4
|
+
*
|
|
5
|
+
* If not provided, defaults to 'drive-api.proton.me'.
|
|
6
|
+
*/
|
|
7
|
+
baseUrl?: string,
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The language to use for error messages.
|
|
11
|
+
*
|
|
12
|
+
* If not provided, defaults to 'en'.
|
|
13
|
+
*/
|
|
14
|
+
language?: string,
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Client UID is used to identify the client for the upload.
|
|
18
|
+
*
|
|
19
|
+
* If the upload failed because of the existing draft, the SDK will
|
|
20
|
+
* automatically clean up the existing draft and start a new upload.
|
|
21
|
+
* If the client UID doesn't match, the SDK throws and then you need
|
|
22
|
+
* to explicitely ask the user to override the existing draft.
|
|
23
|
+
*
|
|
24
|
+
* You can force the upload by setting up
|
|
25
|
+
* `overrideExistingDraftByOtherClient` to true.
|
|
26
|
+
*/
|
|
27
|
+
clientUid?: string,
|
|
28
|
+
}
|
|
@@ -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
|
|
18
|
+
writeToStream(streamFactory: WritableStream, onProgress?: (downloadedBytes: number) => void): DownloadController,
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Same as `writeToStream` but without verification checks.
|
|
22
22
|
*
|
|
23
23
|
* Use this only for debugging purposes.
|
|
24
24
|
*/
|
|
25
|
-
unsafeWriteToStream(streamFactory: WritableStream, onProgress
|
|
25
|
+
unsafeWriteToStream(streamFactory: WritableStream, onProgress?: (downloadedBytes: number) => void): DownloadController,
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
export interface DownloadController {
|
package/src/interface/events.ts
CHANGED
|
@@ -1,30 +1,75 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
export enum SDKEvent {
|
|
2
|
+
TransfersPaused = "transfersPaused",
|
|
3
|
+
TransfersResumed = "transfersResumed",
|
|
4
|
+
RequestsThrottled = "requestsThrottled",
|
|
5
|
+
RequestsUnthrottled = "requestsUnthrottled",
|
|
6
|
+
}
|
|
3
7
|
|
|
4
|
-
export
|
|
5
|
-
|
|
8
|
+
export interface LatestEventIdProvider {
|
|
9
|
+
getLatestEventId(treeEventScopeId: string): string | null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Callback that accepts list of Drive events and flag whether no
|
|
14
|
+
* event should be processed, but rather full cache refresh should be
|
|
15
|
+
* performed.
|
|
16
|
+
*
|
|
17
|
+
* Drive listeners should never throw and be wrapped in a try-catch loop.
|
|
18
|
+
*
|
|
19
|
+
* @param fullRefreshVolumeId - ID of the volume that should be fully refreshed.
|
|
20
|
+
*/
|
|
21
|
+
export type DriveListener = (event: DriveEvent) => Promise<void>;
|
|
22
|
+
|
|
23
|
+
type NodeCruEventType = DriveEventType.NodeCreated | DriveEventType.NodeUpdated;
|
|
6
24
|
|
|
7
25
|
export type NodeEvent = {
|
|
8
|
-
type:
|
|
9
|
-
|
|
10
|
-
|
|
26
|
+
type: NodeCruEventType,
|
|
27
|
+
nodeUid: string,
|
|
28
|
+
parentNodeUid?: string,
|
|
29
|
+
isTrashed: boolean,
|
|
30
|
+
isShared: boolean,
|
|
31
|
+
treeEventScopeId: string,
|
|
32
|
+
eventId: string,
|
|
11
33
|
} | {
|
|
12
|
-
type:
|
|
13
|
-
|
|
34
|
+
type: DriveEventType.NodeDeleted,
|
|
35
|
+
nodeUid: string,
|
|
36
|
+
parentNodeUid?: string,
|
|
37
|
+
treeEventScopeId: string,
|
|
38
|
+
eventId: string,
|
|
14
39
|
}
|
|
15
40
|
|
|
16
|
-
export type
|
|
17
|
-
type:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
} | {
|
|
21
|
-
type: 'remove',
|
|
22
|
-
uid: string,
|
|
41
|
+
export type FastForwardEvent = {
|
|
42
|
+
type: DriveEventType.FastForward,
|
|
43
|
+
treeEventScopeId: string,
|
|
44
|
+
eventId: string,
|
|
23
45
|
}
|
|
24
46
|
|
|
25
|
-
export
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
47
|
+
export type TreeRefreshEvent = {
|
|
48
|
+
type: DriveEventType.TreeRefresh,
|
|
49
|
+
treeEventScopeId: string,
|
|
50
|
+
eventId: string,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export type TreeRemovalEvent = {
|
|
54
|
+
type: DriveEventType.TreeRemove,
|
|
55
|
+
treeEventScopeId: string,
|
|
56
|
+
eventId: 'none',
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export type SharedWithMeUpdated = {
|
|
60
|
+
type: DriveEventType.SharedWithMeUpdated,
|
|
61
|
+
eventId: string,
|
|
62
|
+
treeEventScopeId: 'core',
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export type DriveEvent = NodeEvent | FastForwardEvent | TreeRefreshEvent | TreeRemovalEvent | FastForwardEvent | SharedWithMeUpdated;
|
|
66
|
+
|
|
67
|
+
export enum DriveEventType {
|
|
68
|
+
NodeCreated = 'node_created',
|
|
69
|
+
NodeUpdated = 'node_updated',
|
|
70
|
+
NodeDeleted = 'node_deleted',
|
|
71
|
+
SharedWithMeUpdated = 'shared_with_me_updated',
|
|
72
|
+
TreeRefresh = 'tree_refresh',
|
|
73
|
+
TreeRemove = 'tree_remove',
|
|
74
|
+
FastForward = 'fast_forward'
|
|
30
75
|
}
|
|
@@ -24,19 +24,3 @@ type ProtonDriveHTTPClientBaseOptions = {
|
|
|
24
24
|
timeoutMs: number,
|
|
25
25
|
signal?: AbortSignal,
|
|
26
26
|
}
|
|
27
|
-
|
|
28
|
-
export type ProtonDriveConfig = {
|
|
29
|
-
/**
|
|
30
|
-
* The base URL for the Proton Drive (without schema).
|
|
31
|
-
*
|
|
32
|
-
* If not provided, defaults to 'drive-api.proton.me'.
|
|
33
|
-
*/
|
|
34
|
-
baseUrl?: string,
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* The language to use for error messages.
|
|
38
|
-
*
|
|
39
|
-
* If not provided, defaults to 'en'.
|
|
40
|
-
*/
|
|
41
|
-
language?: string,
|
|
42
|
-
}
|
package/src/interface/index.ts
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import { ProtonDriveCache } from '../cache';
|
|
2
2
|
import { OpenPGPCrypto, PrivateKey, SessionKey, SRPModule } from '../crypto';
|
|
3
|
+
import { LatestEventIdProvider } from '../internal/events/interface';
|
|
3
4
|
import { ProtonDriveAccount } from './account';
|
|
4
|
-
import {
|
|
5
|
+
import { ProtonDriveConfig } from './config';
|
|
6
|
+
import { ProtonDriveHTTPClient } from './httpClient';
|
|
5
7
|
import { Telemetry, MetricEvent } from './telemetry';
|
|
6
8
|
|
|
7
9
|
export type { Result } from './result';
|
|
8
10
|
export { resultOk, resultError } from './result';
|
|
9
11
|
export type { ProtonDriveAccount, ProtonDriveAccountAddress } from './account';
|
|
10
12
|
export type { Author, UnverifiedAuthorError, AnonymousUser } from './author';
|
|
13
|
+
export type { ProtonDriveConfig } from './config';
|
|
11
14
|
export type { Device, DeviceOrUid } from './devices';
|
|
12
15
|
export { DeviceType } from './devices';
|
|
13
16
|
export type { FileDownloader, DownloadController } from './download';
|
|
14
|
-
export type {
|
|
15
|
-
export { SDKEvent } from './events';
|
|
16
|
-
export type { ProtonDriveHTTPClient, ProtonDriveHTTPClientJsonOptions, ProtonDriveHTTPClientBlobOptions
|
|
17
|
+
export type { DriveListener, LatestEventIdProvider, DriveEvent } from './events';
|
|
18
|
+
export { DriveEventType, SDKEvent } from './events';
|
|
19
|
+
export type { ProtonDriveHTTPClient, ProtonDriveHTTPClientJsonOptions, ProtonDriveHTTPClientBlobOptions } from './httpClient';
|
|
17
20
|
export type { MaybeNode, NodeEntity, DegradedNode, MaybeMissingNode, MissingNode, InvalidNameError, Revision, NodeOrUid, RevisionOrUid, NodeResult } from './nodes';
|
|
18
21
|
export { NodeType, MemberRole, RevisionState } from './nodes';
|
|
19
22
|
export type { ProtonInvitation, ProtonInvitationWithNode, NonProtonInvitation, Member, PublicLink, MaybeBookmark, Bookmark, DegradedBookmark, ProtonInvitationOrUid, NonProtonInvitationOrUid, BookmarkOrUid, ShareNodeSettings, UnshareNodeSettings, ShareMembersSettings, SharePublicLinkSettings, SharePublicLinkSettingsObject, ShareResult } from './sharing';
|
|
@@ -43,4 +46,5 @@ export interface ProtonDriveClientContructorParameters {
|
|
|
43
46
|
srpModule: SRPModule,
|
|
44
47
|
config?: ProtonDriveConfig,
|
|
45
48
|
telemetry?: ProtonDriveTelemetry,
|
|
49
|
+
latestEventIdProvider?: LatestEventIdProvider
|
|
46
50
|
};
|
package/src/interface/nodes.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { Author } from './author';
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Node representing a file or folder in the system.
|
|
6
|
-
*
|
|
6
|
+
*
|
|
7
7
|
* This covers both happy path and degraded path. It is used in the SDK to
|
|
8
8
|
* represent the node in a way that is easy to work with. Whenever any field
|
|
9
9
|
* cannot be decrypted, it is returned as `DegradedNode` type.
|
|
@@ -12,7 +12,7 @@ export type MaybeNode = Result<NodeEntity, DegradedNode>;
|
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Node representing a file or folder in the system, or missing node.
|
|
15
|
-
*
|
|
15
|
+
*
|
|
16
16
|
* In most cases, SDK returns `MaybeNode`, but in some specific cases, when
|
|
17
17
|
* client is requesting specific nodes, SDK must return `MissingNode` type
|
|
18
18
|
* to indicate the case when the node is not available. That can be when
|
|
@@ -27,11 +27,11 @@ export type MissingNode = {
|
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Node representing a file or folder in the system.
|
|
30
|
-
*
|
|
30
|
+
*
|
|
31
31
|
* This is a happy path representation of the node. It is used in the SDK to
|
|
32
32
|
* represent the node in a way that is easy to work with. Whenever any field
|
|
33
33
|
* cannot be decrypted, it is returned as `DegradedNode` type.
|
|
34
|
-
*
|
|
34
|
+
*
|
|
35
35
|
* SDK never returns this entity directly but wrapped in `MaybeNode`.
|
|
36
36
|
*
|
|
37
37
|
* Note on naming: Node is reserved by JS/DOM, thus we need exception how the
|
|
@@ -43,7 +43,7 @@ export type NodeEntity = {
|
|
|
43
43
|
name: string,
|
|
44
44
|
/**
|
|
45
45
|
* Author of the node key.
|
|
46
|
-
*
|
|
46
|
+
*
|
|
47
47
|
* Person who created the node and keys for it. If user A uploads the file
|
|
48
48
|
* and user B renames the file and uploads new revision, name and content
|
|
49
49
|
* author is user B, while key author stays to user A who has forever
|
|
@@ -52,7 +52,7 @@ export type NodeEntity = {
|
|
|
52
52
|
keyAuthor: Author,
|
|
53
53
|
/**
|
|
54
54
|
* Author of the name.
|
|
55
|
-
*
|
|
55
|
+
*
|
|
56
56
|
* Person who named the file. If user A uploads the file and user B renames
|
|
57
57
|
* the file, key and content author is user A, while name author is user B.
|
|
58
58
|
*/
|
|
@@ -87,20 +87,29 @@ export type NodeEntity = {
|
|
|
87
87
|
folder?: {
|
|
88
88
|
claimedModificationTime?: Date,
|
|
89
89
|
},
|
|
90
|
+
/**
|
|
91
|
+
* Provides an ID for the event scope.
|
|
92
|
+
*
|
|
93
|
+
* By subscribing to events in a scope, all updates to nodes
|
|
94
|
+
* withing that scope will be passed to the client. The scope can
|
|
95
|
+
* comprise one or more folder trees and will be shared by all
|
|
96
|
+
* nodes in the tree. Nodes cannot change scopes.
|
|
97
|
+
*/
|
|
98
|
+
treeEventScopeId: string,
|
|
90
99
|
}
|
|
91
100
|
|
|
92
101
|
/**
|
|
93
102
|
* Degraded node representing a file or folder in the system.
|
|
94
|
-
*
|
|
103
|
+
*
|
|
95
104
|
* This is a degraded path representation of the node. It is used in the SDK to
|
|
96
105
|
* represent the node in a way that is easy to work with. Whenever any field
|
|
97
106
|
* cannot be decrypted, it is returned as `DegradedNode` type.
|
|
98
|
-
*
|
|
107
|
+
*
|
|
99
108
|
* SDK never returns this entity directly but wrapped in `MaybeNode`.
|
|
100
|
-
*
|
|
109
|
+
*
|
|
101
110
|
* The node can be still used around, but it is not guaranteed that all
|
|
102
111
|
* properties are decrypted, or that all actions can be performed on it.
|
|
103
|
-
*
|
|
112
|
+
*
|
|
104
113
|
* For example, if the node has issue decrypting the name, the name will be
|
|
105
114
|
* set as `Error` and potentially rename or move actions will not be
|
|
106
115
|
* possible, but download and upload new revision will still work.
|
|
@@ -110,10 +119,10 @@ export type DegradedNode = Omit<NodeEntity, 'name' | 'activeRevision'> & {
|
|
|
110
119
|
activeRevision?: Result<Revision, Error>,
|
|
111
120
|
/**
|
|
112
121
|
* If the error is not related to any specific field, it is set here.
|
|
113
|
-
*
|
|
122
|
+
*
|
|
114
123
|
* For example, if the node has issue decrypting the name, the name will be
|
|
115
124
|
* set as `Error` while this will be empty.
|
|
116
|
-
*
|
|
125
|
+
*
|
|
117
126
|
* On the other hand, if the node has issue decrypting the node key, but
|
|
118
127
|
* the name is still working, this will include the node key error, while
|
|
119
128
|
* the name will be set to the decrypted value.
|
package/src/interface/sharing.ts
CHANGED
package/src/interface/upload.ts
CHANGED
|
@@ -24,6 +24,12 @@ export type UploadMetadata = {
|
|
|
24
24
|
* The metadata will be encrypted and stored with the file.
|
|
25
25
|
*/
|
|
26
26
|
additionalMetadata?: object,
|
|
27
|
+
/**
|
|
28
|
+
* If there is an existing draft by another client, the upload will be
|
|
29
|
+
* rejected. If user decides to override the existing draft and continue
|
|
30
|
+
* with the upload, set this to true.
|
|
31
|
+
*/
|
|
32
|
+
overrideExistingDraftByOtherClient?: boolean,
|
|
27
33
|
};
|
|
28
34
|
|
|
29
35
|
export interface FileRevisionUploader {
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { c } from "ttag";
|
|
2
|
-
import { ValidationError } from "../../errors";
|
|
3
1
|
import { DriveAPIService, drivePaths, ObserverStream } from "../apiService";
|
|
4
2
|
import { makeNodeThumbnailUid, splitNodeRevisionUid, splitNodeThumbnailUid } from "../uids";
|
|
5
3
|
import { BlockMetadata } from "./interface";
|
|
@@ -85,19 +83,22 @@ export class DownloadAPIService {
|
|
|
85
83
|
return encryptedBlock;
|
|
86
84
|
}
|
|
87
85
|
|
|
88
|
-
// Improvement requested: support multiple volumes.
|
|
89
86
|
async* iterateThumbnails(thumbnailUids: string[], signal?: AbortSignal): AsyncGenerator<
|
|
90
87
|
{ uid: string, ok: true, bareUrl: string, token: string } |
|
|
91
88
|
{ uid: string, ok: false, error: string }
|
|
92
89
|
> {
|
|
93
|
-
const
|
|
90
|
+
const splitedThumbnailsIds = thumbnailUids.map(splitNodeThumbnailUid);
|
|
94
91
|
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
92
|
+
const thumbnailIdsByVolumeId = new Map<string, { volumeId: string, thumbnailId: string, nodeId: string }[]>();
|
|
93
|
+
for (const { volumeId, thumbnailId, nodeId } of splitedThumbnailsIds) {
|
|
94
|
+
if (!thumbnailIdsByVolumeId.has(volumeId)) {
|
|
95
|
+
thumbnailIdsByVolumeId.set(volumeId, []);
|
|
96
|
+
}
|
|
97
|
+
thumbnailIdsByVolumeId.get(volumeId)?.push({ volumeId, thumbnailId, nodeId });
|
|
98
98
|
}
|
|
99
|
-
const volumeId = thumbnailIds[0].volumeId;
|
|
100
99
|
|
|
100
|
+
|
|
101
|
+
for (const [volumeId, thumbnailIds] of thumbnailIdsByVolumeId.entries()) {
|
|
101
102
|
const result = await this.apiService.post<PostGetThumbnailsRequest, PostGetThumbnailsResponse>(
|
|
102
103
|
`drive/volumes/${volumeId}/thumbnails`,
|
|
103
104
|
{
|
|
@@ -130,6 +131,8 @@ export class DownloadAPIService {
|
|
|
130
131
|
error: error.Error,
|
|
131
132
|
};
|
|
132
133
|
}
|
|
134
|
+
|
|
135
|
+
}
|
|
133
136
|
}
|
|
134
137
|
}
|
|
135
138
|
|
|
@@ -49,7 +49,7 @@ export class FileDownloader {
|
|
|
49
49
|
return this.revision.claimedSize;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
writeToStream(stream: WritableStream, onProgress
|
|
52
|
+
writeToStream(stream: WritableStream, onProgress?: (downloadedBytes: number) => void): DownloadController {
|
|
53
53
|
if (this.controller.promise) {
|
|
54
54
|
throw new Error(`Download already started`);
|
|
55
55
|
}
|
|
@@ -57,7 +57,7 @@ export class FileDownloader {
|
|
|
57
57
|
return this.controller;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
unsafeWriteToStream(stream: WritableStream, onProgress
|
|
60
|
+
unsafeWriteToStream(stream: WritableStream, onProgress?: (downloadedBytes: number) => void): DownloadController {
|
|
61
61
|
if (this.controller.promise) {
|
|
62
62
|
throw new Error(`Download already started`);
|
|
63
63
|
}
|