@axium/client 0.35.0 → 0.36.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/uploads.d.ts +16 -0
- package/dist/uploads.js +111 -0
- package/package.json +2 -2
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type ProgressHandler = (this: void, uploaded: number, total: number) => void;
|
|
2
|
+
export interface UploadChunkedOptions {
|
|
3
|
+
endpoint: string | URL;
|
|
4
|
+
/** Upload token */
|
|
5
|
+
token: string;
|
|
6
|
+
stream: ReadableStream<Uint8Array<ArrayBuffer>>;
|
|
7
|
+
itemSize: number;
|
|
8
|
+
/** Maximum size of a request body in MiB */
|
|
9
|
+
maxTransferSize: number;
|
|
10
|
+
onProgress?: ProgressHandler;
|
|
11
|
+
signal?: AbortSignal;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Does a chunked upload to a given endpoint
|
|
15
|
+
*/
|
|
16
|
+
export declare function uploadChunked<T>(options: UploadChunkedOptions): Promise<T>;
|
package/dist/uploads.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { warnOnce } from 'ioium';
|
|
2
|
+
import { token } from './requests.js';
|
|
3
|
+
function handleFetchFailed(e) {
|
|
4
|
+
if (!(e instanceof Error) || e.message != 'fetch failed')
|
|
5
|
+
throw e;
|
|
6
|
+
throw 'fetch failed: ' + String(e.cause);
|
|
7
|
+
}
|
|
8
|
+
async function handleError(response) {
|
|
9
|
+
if (response.headers.get('Content-Type')?.trim() != 'application/json')
|
|
10
|
+
throw await response.text();
|
|
11
|
+
const json = await response.json();
|
|
12
|
+
throw json.message;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Does a chunked upload to a given endpoint
|
|
16
|
+
*/
|
|
17
|
+
export async function uploadChunked(options) {
|
|
18
|
+
const { endpoint, token: uploadToken, stream, itemSize, maxTransferSize, onProgress, signal } = options;
|
|
19
|
+
signal?.addEventListener('abort', () => {
|
|
20
|
+
void fetch(endpoint, {
|
|
21
|
+
method: 'DELETE',
|
|
22
|
+
headers: { 'x-upload': uploadToken },
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
const targetChunkSize = maxTransferSize * 1_000_000;
|
|
26
|
+
let response;
|
|
27
|
+
const reader = stream.getReader();
|
|
28
|
+
let buffer = new Uint8Array(0);
|
|
29
|
+
for (let offset = 0; offset < itemSize; offset += targetChunkSize) {
|
|
30
|
+
signal?.throwIfAborted();
|
|
31
|
+
const chunkSize = Math.min(targetChunkSize, itemSize - offset);
|
|
32
|
+
let bytesReadForChunk = 0;
|
|
33
|
+
const headers = {
|
|
34
|
+
'x-upload': uploadToken,
|
|
35
|
+
'x-offset': offset.toString(),
|
|
36
|
+
'x-chunk-size': chunkSize.toString(),
|
|
37
|
+
'content-length': chunkSize.toString(),
|
|
38
|
+
'content-type': 'application/octet-stream',
|
|
39
|
+
};
|
|
40
|
+
if (token)
|
|
41
|
+
headers.authorization = 'Bearer ' + token;
|
|
42
|
+
let body = new ReadableStream({
|
|
43
|
+
async pull(controller) {
|
|
44
|
+
if (bytesReadForChunk >= chunkSize) {
|
|
45
|
+
controller.close();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (!buffer.length) {
|
|
49
|
+
const { done, value } = await reader.read();
|
|
50
|
+
if (done) {
|
|
51
|
+
controller.close();
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
buffer = value;
|
|
55
|
+
}
|
|
56
|
+
const take = Math.min(buffer.length, chunkSize - bytesReadForChunk);
|
|
57
|
+
const chunk = buffer.subarray(0, take);
|
|
58
|
+
buffer = buffer.subarray(take);
|
|
59
|
+
bytesReadForChunk += take;
|
|
60
|
+
controller.enqueue(chunk);
|
|
61
|
+
onProgress?.(offset + bytesReadForChunk, itemSize);
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
let init = { duplex: 'half' };
|
|
65
|
+
/** @see https://bugzilla.mozilla.org/show_bug.cgi?id=1387483 */
|
|
66
|
+
if (globalThis.navigator?.userAgent?.toLowerCase().includes('firefox')) {
|
|
67
|
+
await body.cancel();
|
|
68
|
+
init = {};
|
|
69
|
+
warnOnce('Using a workaround for uploading on Firefox [https://bugzilla.mozilla.org/show_bug.cgi?id=1387483]');
|
|
70
|
+
const chunkData = new Uint8Array(chunkSize);
|
|
71
|
+
let bytesReadForChunk = 0;
|
|
72
|
+
if (buffer.length > 0) {
|
|
73
|
+
const take = Math.min(buffer.length, chunkSize);
|
|
74
|
+
chunkData.set(buffer.subarray(0, take), 0);
|
|
75
|
+
buffer = buffer.subarray(take);
|
|
76
|
+
bytesReadForChunk += take;
|
|
77
|
+
}
|
|
78
|
+
while (bytesReadForChunk < chunkSize) {
|
|
79
|
+
const { done, value } = await reader.read();
|
|
80
|
+
if (done)
|
|
81
|
+
break;
|
|
82
|
+
const take = Math.min(value.length, chunkSize - bytesReadForChunk);
|
|
83
|
+
chunkData.set(value.subarray(0, take), bytesReadForChunk);
|
|
84
|
+
buffer = value.subarray(take);
|
|
85
|
+
bytesReadForChunk += take;
|
|
86
|
+
}
|
|
87
|
+
body = chunkData.subarray(0, bytesReadForChunk);
|
|
88
|
+
onProgress?.(offset + bytesReadForChunk, itemSize);
|
|
89
|
+
}
|
|
90
|
+
response = await fetch(endpoint, {
|
|
91
|
+
method: 'POST',
|
|
92
|
+
headers,
|
|
93
|
+
body,
|
|
94
|
+
signal,
|
|
95
|
+
...init,
|
|
96
|
+
}).catch(handleFetchFailed);
|
|
97
|
+
if (!response.ok)
|
|
98
|
+
await handleError(response);
|
|
99
|
+
if (offset + chunkSize != itemSize && response.status != 204)
|
|
100
|
+
console.warn('Unexpected end of upload before last chunk');
|
|
101
|
+
}
|
|
102
|
+
if (!response)
|
|
103
|
+
throw new Error('BUG: No response');
|
|
104
|
+
if (!response.headers.get('Content-Type')?.includes('application/json')) {
|
|
105
|
+
throw new Error(`Unexpected response type: ${response.headers.get('Content-Type')}`);
|
|
106
|
+
}
|
|
107
|
+
const json = await response.json().catch(() => ({ message: 'Unknown server error (invalid JSON response)' }));
|
|
108
|
+
if (!response.ok)
|
|
109
|
+
await handleError(response);
|
|
110
|
+
return json;
|
|
111
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axium/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.36.0",
|
|
4
4
|
"author": "James Prevett <jp@jamespre.dev>",
|
|
5
5
|
"funding": {
|
|
6
6
|
"type": "individual",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"build": "tsc"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
|
-
"@axium/core": ">=0.
|
|
52
|
+
"@axium/core": ">=0.38.0",
|
|
53
53
|
"ioium": "^1.7.0",
|
|
54
54
|
"semver": "^7.7.4",
|
|
55
55
|
"svelte": "^5.36.0",
|