@secrecy/lib 1.83.0 → 1.83.2-fix-upload-data-error-handling.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.
- package/dist/lib/client/SecrecyCloudClient.js +36 -26
- package/dist/lib/client/upload.js +26 -6
- package/dist/lib/crypto/domain.js +3 -2
- package/dist/types/client/SecrecyCloudClient.d.ts +8 -2
- package/package.json +1 -1
- package/dist/lib/utils/promise.js +0 -20
- package/dist/types/utils/promise.d.ts +0 -1
|
@@ -179,19 +179,24 @@ export class SecrecyCloudClient {
|
|
|
179
179
|
deleted,
|
|
180
180
|
fromIdentityPubKey: this.#client.currentIdentity.identityPubKey,
|
|
181
181
|
});
|
|
182
|
-
const errors = apiNodes.missingIds.map((id) =>
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
182
|
+
const errors = apiNodes.missingIds.map((id) => ({
|
|
183
|
+
message: `Node ${id} not found`,
|
|
184
|
+
nodeId: id,
|
|
185
|
+
}));
|
|
186
|
+
const nodes = [];
|
|
187
|
+
const handleNode = async (node) => {
|
|
188
|
+
try {
|
|
189
|
+
const externalNode = await apiNodeToExternal(node, this.#client.keyPairs);
|
|
190
|
+
nodes.push(externalNode);
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
errors.push({
|
|
194
|
+
message: error instanceof Error ? error.message : String(error),
|
|
195
|
+
nodeId: node.id,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
await Promise.all(apiNodes.nodes.map(handleNode));
|
|
195
200
|
return { nodes, errors };
|
|
196
201
|
}
|
|
197
202
|
async nodesFull({ ids, deleted, }) {
|
|
@@ -200,19 +205,24 @@ export class SecrecyCloudClient {
|
|
|
200
205
|
deleted,
|
|
201
206
|
fromIdentityPubKey: this.#client.currentIdentity.identityPubKey,
|
|
202
207
|
});
|
|
203
|
-
const errors = apiNodes.missingIds.map((id) =>
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
208
|
+
const errors = apiNodes.missingIds.map((id) => ({
|
|
209
|
+
message: `Node ${id} not found`,
|
|
210
|
+
nodeId: id,
|
|
211
|
+
}));
|
|
212
|
+
const nodes = [];
|
|
213
|
+
const handleNode = async (node) => {
|
|
214
|
+
try {
|
|
215
|
+
const externalNode = await apiNodeToExternalNodeFull(node, this.#client.keyPairs);
|
|
216
|
+
nodes.push(externalNode);
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
errors.push({
|
|
220
|
+
message: error instanceof Error ? error.message : String(error),
|
|
221
|
+
nodeId: node.id,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
await Promise.all(apiNodes.nodes.map(handleNode));
|
|
216
226
|
return { nodes, errors };
|
|
217
227
|
}
|
|
218
228
|
async dataMetadata({ id }) {
|
|
@@ -7,7 +7,6 @@ import { dataContentCache } from '../cache.js';
|
|
|
7
7
|
import { chunks, enumerate } from '../utils/array.js';
|
|
8
8
|
import { getTrpcGuestClient, } from '../client.js';
|
|
9
9
|
import axios from 'axios';
|
|
10
|
-
import { promiseAllLimit } from '../utils/promise.js';
|
|
11
10
|
import { encryptDataAndKey } from '../crypto/domain.js';
|
|
12
11
|
import { derivePassword, generatePassword } from '../crypto/helpers.js';
|
|
13
12
|
import { decryptCryptoBox, encryptSecretBox } from '../crypto/index.js';
|
|
@@ -213,13 +212,13 @@ export async function uploadData({ storageType, data, password, forcePassword =
|
|
|
213
212
|
const formData = new FormData();
|
|
214
213
|
const chunk = chunkParts.find((p) => p.order === part.order);
|
|
215
214
|
if (chunk === undefined) {
|
|
216
|
-
|
|
215
|
+
throw new Error(`Unable to find chunk data for part ${part.order} of data ${uploadData.id}`);
|
|
217
216
|
}
|
|
218
217
|
for (const [key, value] of Object.entries(part.fields)) {
|
|
219
218
|
formData.append(key, value);
|
|
220
219
|
}
|
|
221
220
|
formData.append('file', new Blob([chunk.data], { type: filetype?.mime }), `${uploadData.id}-${chunk.order}`);
|
|
222
|
-
await axios.post(part.url, formData, {
|
|
221
|
+
const response = await axios.post(part.url, formData, {
|
|
223
222
|
onUploadProgress: (progressEvent) => {
|
|
224
223
|
onProgress(part.order, {
|
|
225
224
|
percent: progressEvent.progress ?? 0,
|
|
@@ -227,12 +226,33 @@ export async function uploadData({ storageType, data, password, forcePassword =
|
|
|
227
226
|
transferredBytes: progressEvent.loaded,
|
|
228
227
|
});
|
|
229
228
|
},
|
|
229
|
+
signal,
|
|
230
230
|
});
|
|
231
|
+
// TODO implement retry etc
|
|
232
|
+
if (response.status !== 204 && response.status !== 201) {
|
|
233
|
+
throw new Error(`Failed to upload data part ${part.order} of data ${uploadData.id}`);
|
|
234
|
+
}
|
|
231
235
|
return uploadDataPartEnd(chunk.md5, chunk.order);
|
|
232
236
|
};
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
237
|
+
let lastValidatedCalled = false;
|
|
238
|
+
for (const part of uploadData.parts) {
|
|
239
|
+
const res = await byPart(part);
|
|
240
|
+
if (!res.isUploadPartEnded) {
|
|
241
|
+
throw new Error(`Failed to confirm upload of part ${part.order} of data ${uploadData.id}`);
|
|
242
|
+
}
|
|
243
|
+
if (res.isLastValidatedPart) {
|
|
244
|
+
lastValidatedCalled = true;
|
|
245
|
+
await uploadProgress?.({
|
|
246
|
+
total: encryptedData.byteLength,
|
|
247
|
+
current: encryptedData.byteLength,
|
|
248
|
+
percent: 1,
|
|
249
|
+
});
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
if (!lastValidatedCalled) {
|
|
254
|
+
throw new Error(`Failed to validate the last part of data ${uploadData.id}`);
|
|
255
|
+
}
|
|
236
256
|
const sharing = createSharing({ data: uploadData });
|
|
237
257
|
const localData = {
|
|
238
258
|
id: uploadData.id,
|
|
@@ -3,7 +3,6 @@ import { secretStreamKeygen } from './data';
|
|
|
3
3
|
import { encrypt } from '../worker/sodium';
|
|
4
4
|
import ky from 'ky';
|
|
5
5
|
import { md5 } from '../worker/md5.js';
|
|
6
|
-
import { promiseAllLimit } from '../utils/promise.js';
|
|
7
6
|
import { concatenate } from '../utils/array.js';
|
|
8
7
|
/**
|
|
9
8
|
* Encrypt the dataKey and the data as logged or guest user.
|
|
@@ -53,7 +52,9 @@ const encryptedContentFromParts = async (arg) => {
|
|
|
53
52
|
parts[arg.dataId] ??= [];
|
|
54
53
|
parts[arg.dataId].push({ data: buf, order: part.order });
|
|
55
54
|
};
|
|
56
|
-
|
|
55
|
+
for (const part of arg.dataParts) {
|
|
56
|
+
await byPart(part);
|
|
57
|
+
}
|
|
57
58
|
return concatenate(...parts[arg.dataId].sort((a, b) => a.order - b.order).map((p) => p.data));
|
|
58
59
|
};
|
|
59
60
|
export async function buildBytesFromApiData({ dataContent, totalBytes, progressParts, onDownloadProgress, signal, }) {
|
|
@@ -61,14 +61,20 @@ export declare class SecrecyCloudClient {
|
|
|
61
61
|
deleted?: boolean | null | undefined;
|
|
62
62
|
}): Promise<{
|
|
63
63
|
nodes: Node[];
|
|
64
|
-
errors:
|
|
64
|
+
errors: {
|
|
65
|
+
message: string;
|
|
66
|
+
nodeId: string;
|
|
67
|
+
}[];
|
|
65
68
|
}>;
|
|
66
69
|
nodesFull({ ids, deleted, }: {
|
|
67
70
|
ids: string[];
|
|
68
71
|
deleted?: boolean | null | undefined;
|
|
69
72
|
}): Promise<{
|
|
70
73
|
nodes: NodeFull[];
|
|
71
|
-
errors:
|
|
74
|
+
errors: {
|
|
75
|
+
message: string;
|
|
76
|
+
nodeId: string;
|
|
77
|
+
}[];
|
|
72
78
|
}>;
|
|
73
79
|
dataMetadata({ id }: {
|
|
74
80
|
id: string;
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@secrecy/lib",
|
|
3
3
|
"author": "Anonymize <anonymize@gmail.com>",
|
|
4
4
|
"description": "Anonymize Secrecy Library",
|
|
5
|
-
"version": "1.83.
|
|
5
|
+
"version": "1.83.2-fix-upload-data-error-handling.1",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "https://github.com/anonymize-org/lib.git"
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export const promiseAllLimit = async (n, list) => {
|
|
2
|
-
const head = list.slice(0, n);
|
|
3
|
-
const tail = list.slice(n);
|
|
4
|
-
const result = [];
|
|
5
|
-
const execute = async (promise, i, runNext) => {
|
|
6
|
-
result[i] = await promise();
|
|
7
|
-
await runNext();
|
|
8
|
-
};
|
|
9
|
-
const runNext = async () => {
|
|
10
|
-
const i = list.length - tail.length;
|
|
11
|
-
const promise = tail.shift();
|
|
12
|
-
if (promise !== undefined) {
|
|
13
|
-
await execute(promise, i, runNext);
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
|
-
await Promise.all(head.map(async (promise, i) => {
|
|
17
|
-
await execute(promise, i, runNext);
|
|
18
|
-
}));
|
|
19
|
-
return result;
|
|
20
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const promiseAllLimit: <T>(n: number, list: Array<() => Promise<T>>) => Promise<T[]>;
|