@knime/product-features 2.2.2 → 2.3.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/CHANGELOG.md +23 -0
- package/dist/core-CVWznjtP.js +739 -0
- package/dist/core-CVWznjtP.js.map +1 -0
- package/dist/dataTransfer/core.js +2 -0
- package/dist/dataTransfer.css +12 -12
- package/dist/dataTransfer.js +420 -767
- package/dist/dataTransfer.js.map +1 -1
- package/dist/src/dataTransfer/core.d.ts +2 -0
- package/dist/src/dataTransfer/core.d.ts.map +1 -0
- package/dist/src/dataTransfer/index.d.ts +1 -2
- package/dist/src/dataTransfer/index.d.ts.map +1 -1
- package/dist/src/dataTransfer/upload/{ui/UploadProgressPanel.vue.d.ts → UploadProgressPanel.vue.d.ts} +2 -0
- package/dist/src/dataTransfer/upload/UploadProgressPanel.vue.d.ts.map +1 -0
- package/dist/src/dataTransfer/upload/{ui/UploadProgressPanelItem.vue.d.ts → UploadProgressPanelItem.vue.d.ts} +2 -1
- package/dist/src/dataTransfer/upload/UploadProgressPanelItem.vue.d.ts.map +1 -0
- package/dist/src/dataTransfer/upload/core/index.d.ts +3 -0
- package/dist/src/dataTransfer/upload/core/index.d.ts.map +1 -0
- package/dist/src/dataTransfer/upload/core/uploadClient/createUploadStatusPoller.d.ts +26 -0
- package/dist/src/dataTransfer/upload/core/uploadClient/createUploadStatusPoller.d.ts.map +1 -0
- package/dist/src/dataTransfer/upload/core/uploadClient/index.d.ts +3 -0
- package/dist/src/dataTransfer/upload/core/uploadClient/index.d.ts.map +1 -0
- package/dist/src/dataTransfer/upload/core/uploadClient/uploadClient.d.ts +59 -0
- package/dist/src/dataTransfer/upload/core/uploadClient/uploadClient.d.ts.map +1 -0
- package/dist/src/dataTransfer/upload/{manager/index.d.ts → core/uploadManager/chunkedUploadManager.d.ts} +21 -60
- package/dist/src/dataTransfer/upload/core/uploadManager/chunkedUploadManager.d.ts.map +1 -0
- package/dist/src/dataTransfer/upload/core/uploadManager/config.d.ts +12 -0
- package/dist/src/dataTransfer/upload/core/uploadManager/config.d.ts.map +1 -0
- package/dist/src/dataTransfer/upload/core/uploadManager/index.d.ts +5 -0
- package/dist/src/dataTransfer/upload/core/uploadManager/index.d.ts.map +1 -0
- package/dist/src/dataTransfer/upload/core/uploadManager/state.d.ts +30 -0
- package/dist/src/dataTransfer/upload/core/uploadManager/state.d.ts.map +1 -0
- package/dist/src/dataTransfer/upload/core/uploadManager/streamingUploadManager.d.ts +52 -0
- package/dist/src/dataTransfer/upload/core/uploadManager/streamingUploadManager.d.ts.map +1 -0
- package/dist/src/dataTransfer/upload/core/uploadManager/types.d.ts +46 -0
- package/dist/src/dataTransfer/upload/core/uploadManager/types.d.ts.map +1 -0
- package/dist/src/dataTransfer/upload/core/uploadManager/uploadPartBytes.d.ts +17 -0
- package/dist/src/dataTransfer/upload/core/uploadManager/uploadPartBytes.d.ts.map +1 -0
- package/dist/src/dataTransfer/upload/index.d.ts +6 -0
- package/dist/src/dataTransfer/upload/index.d.ts.map +1 -0
- package/dist/src/dataTransfer/upload/types.d.ts +80 -0
- package/dist/src/dataTransfer/upload/types.d.ts.map +1 -0
- package/dist/src/dataTransfer/upload/useFileUpload.d.ts +5 -13
- package/dist/src/dataTransfer/upload/useFileUpload.d.ts.map +1 -1
- package/dist/src/dataTransfer/upload/{ui/useUploadManager.d.ts → useUploadManager.d.ts} +17 -5
- package/dist/src/dataTransfer/upload/useUploadManager.d.ts.map +1 -0
- package/package.json +8 -4
- package/dist/src/dataTransfer/upload/manager/index.d.ts.map +0 -1
- package/dist/src/dataTransfer/upload/ui/UploadProgressPanel.vue.d.ts.map +0 -1
- package/dist/src/dataTransfer/upload/ui/UploadProgressPanelItem.vue.d.ts.map +0 -1
- package/dist/src/dataTransfer/upload/ui/index.d.ts +0 -5
- package/dist/src/dataTransfer/upload/ui/index.d.ts.map +0 -1
- package/dist/src/dataTransfer/upload/ui/types.d.ts +0 -60
- package/dist/src/dataTransfer/upload/ui/types.d.ts.map +0 -1
- package/dist/src/dataTransfer/upload/ui/useUploadManager.d.ts.map +0 -1
|
@@ -0,0 +1,739 @@
|
|
|
1
|
+
import { c as promise_exports, l as sleep, o as getFileMimeType } from "./dist-DvDWkkNg.js";
|
|
2
|
+
import { ofetch } from "ofetch";
|
|
3
|
+
//#region ../../node_modules/.pnpm/es-toolkit@1.49.0/node_modules/es-toolkit/dist/predicate/isPlainObject.mjs
|
|
4
|
+
/**
|
|
5
|
+
* Checks if a given value is a plain object.
|
|
6
|
+
*
|
|
7
|
+
* @param value - The value to check.
|
|
8
|
+
* @returns True if the value is a plain object, otherwise false.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* // ✅👇 True
|
|
13
|
+
*
|
|
14
|
+
* isPlainObject({ }); // ✅
|
|
15
|
+
* isPlainObject({ key: 'value' }); // ✅
|
|
16
|
+
* isPlainObject({ key: new Date() }); // ✅
|
|
17
|
+
* isPlainObject(new Object()); // ✅
|
|
18
|
+
* isPlainObject(Object.create(null)); // ✅
|
|
19
|
+
* isPlainObject({ nested: { key: true} }); // ✅
|
|
20
|
+
* isPlainObject(new Proxy({}, {})); // ✅
|
|
21
|
+
* isPlainObject({ [Symbol('tag')]: 'A' }); // ✅
|
|
22
|
+
*
|
|
23
|
+
* // ✅👇 (cross-realms, node context, workers, ...)
|
|
24
|
+
* const runInNewContext = await import('node:vm').then(
|
|
25
|
+
* (mod) => mod.runInNewContext
|
|
26
|
+
* );
|
|
27
|
+
* isPlainObject(runInNewContext('({})')); // ✅
|
|
28
|
+
*
|
|
29
|
+
* // ❌👇 False
|
|
30
|
+
*
|
|
31
|
+
* class Test { };
|
|
32
|
+
* isPlainObject(new Test()) // ❌
|
|
33
|
+
* isPlainObject(10); // ❌
|
|
34
|
+
* isPlainObject(null); // ❌
|
|
35
|
+
* isPlainObject('hello'); // ❌
|
|
36
|
+
* isPlainObject([]); // ❌
|
|
37
|
+
* isPlainObject(new Date()); // ❌
|
|
38
|
+
* isPlainObject(new Uint8Array([1])); // ❌
|
|
39
|
+
* isPlainObject(Buffer.from('ABC')); // ❌
|
|
40
|
+
* isPlainObject(Promise.resolve({})); // ❌
|
|
41
|
+
* isPlainObject(Object.create({})); // ❌
|
|
42
|
+
* isPlainObject(new (class Cls {})); // ❌
|
|
43
|
+
* isPlainObject(globalThis); // ❌,
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
function isPlainObject(value) {
|
|
47
|
+
if (!value || typeof value !== "object") return false;
|
|
48
|
+
const proto = Object.getPrototypeOf(value);
|
|
49
|
+
if (!(proto === null || proto === Object.prototype || Object.getPrototypeOf(proto) === null)) return false;
|
|
50
|
+
return Object.prototype.toString.call(value) === "[object Object]";
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
//#region ../../node_modules/.pnpm/es-toolkit@1.49.0/node_modules/es-toolkit/dist/_internal/isUnsafeProperty.mjs
|
|
54
|
+
/**
|
|
55
|
+
* Checks if a property key is unsafe to modify directly.
|
|
56
|
+
*
|
|
57
|
+
* This function is used in functions like `merge` to prevent prototype pollution attacks
|
|
58
|
+
* by identifying property keys that could modify the object's prototype chain or constructor.
|
|
59
|
+
*
|
|
60
|
+
* @param key - The property key to check
|
|
61
|
+
* @returns `true` if the property is unsafe to modify directly, `false` otherwise
|
|
62
|
+
* @internal
|
|
63
|
+
*/
|
|
64
|
+
function isUnsafeProperty(key) {
|
|
65
|
+
return key === "__proto__";
|
|
66
|
+
}
|
|
67
|
+
//#endregion
|
|
68
|
+
//#region ../../node_modules/.pnpm/es-toolkit@1.49.0/node_modules/es-toolkit/dist/object/merge.mjs
|
|
69
|
+
/**
|
|
70
|
+
* Merges the properties of the source object into the target object.
|
|
71
|
+
*
|
|
72
|
+
* This function performs a deep merge, meaning nested objects and arrays are merged recursively.
|
|
73
|
+
* If a property in the source object is an array or an object and the corresponding property in the target object is also an array or object, they will be merged.
|
|
74
|
+
* If a property in the source object is undefined, it will not overwrite a defined property in the target object.
|
|
75
|
+
*
|
|
76
|
+
* Note that this function mutates the target object.
|
|
77
|
+
*
|
|
78
|
+
* @param target - The target object into which the source object properties will be merged. This object is modified in place.
|
|
79
|
+
* @param source - The source object whose properties will be merged into the target object.
|
|
80
|
+
* @returns The updated target object with properties from the source object merged in.
|
|
81
|
+
*
|
|
82
|
+
* @template T - Type of the target object.
|
|
83
|
+
* @template S - Type of the source object.
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* const target = { a: 1, b: { x: 1, y: 2 } };
|
|
87
|
+
* const source = { b: { y: 3, z: 4 }, c: 5 };
|
|
88
|
+
*
|
|
89
|
+
* const result = merge(target, source);
|
|
90
|
+
* console.log(result);
|
|
91
|
+
* // Output: { a: 1, b: { x: 1, y: 3, z: 4 }, c: 5 }
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* const target = { a: [1, 2], b: { x: 1 } };
|
|
95
|
+
* const source = { a: [3], b: { y: 2 } };
|
|
96
|
+
*
|
|
97
|
+
* const result = merge(target, source);
|
|
98
|
+
* console.log(result);
|
|
99
|
+
* // Output: { a: [3, 2], b: { x: 1, y: 2 } }
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* const target = { a: null };
|
|
103
|
+
* const source = { a: [1, 2, 3] };
|
|
104
|
+
*
|
|
105
|
+
* const result = merge(target, source);
|
|
106
|
+
* console.log(result);
|
|
107
|
+
* // Output: { a: [1, 2, 3] }
|
|
108
|
+
*/
|
|
109
|
+
function merge(target, source) {
|
|
110
|
+
const sourceKeys = Object.keys(source);
|
|
111
|
+
for (let i = 0; i < sourceKeys.length; i++) {
|
|
112
|
+
const key = sourceKeys[i];
|
|
113
|
+
if (isUnsafeProperty(key)) continue;
|
|
114
|
+
const sourceValue = source[key];
|
|
115
|
+
const targetValue = target[key];
|
|
116
|
+
if (isMergeableValue(sourceValue) && isMergeableValue(targetValue)) target[key] = merge(targetValue, sourceValue);
|
|
117
|
+
else if (Array.isArray(sourceValue)) target[key] = merge([], sourceValue);
|
|
118
|
+
else if (isPlainObject(sourceValue)) target[key] = merge({}, sourceValue);
|
|
119
|
+
else if (targetValue === void 0 || sourceValue !== void 0) target[key] = sourceValue;
|
|
120
|
+
}
|
|
121
|
+
return target;
|
|
122
|
+
}
|
|
123
|
+
function isMergeableValue(value) {
|
|
124
|
+
return isPlainObject(value) || Array.isArray(value);
|
|
125
|
+
}
|
|
126
|
+
//#endregion
|
|
127
|
+
//#region src/dataTransfer/shared/httpClient/constants.ts
|
|
128
|
+
var API_BASE_URL = "/_/api";
|
|
129
|
+
//#endregion
|
|
130
|
+
//#region src/dataTransfer/shared/httpClient/createHttpClient.ts
|
|
131
|
+
var defaultConfig = {
|
|
132
|
+
baseURL: API_BASE_URL,
|
|
133
|
+
headers: {
|
|
134
|
+
"Content-Type": "application/json",
|
|
135
|
+
Accept: "application/json; application/problem+json"
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
var createHttpClient = (customOptions) => {
|
|
139
|
+
const { baseURL = API_BASE_URL, ...otherCustomOptions } = customOptions ?? {};
|
|
140
|
+
return ofetch.create(merge({
|
|
141
|
+
baseURL,
|
|
142
|
+
...otherCustomOptions
|
|
143
|
+
}, defaultConfig));
|
|
144
|
+
};
|
|
145
|
+
//#endregion
|
|
146
|
+
//#region src/dataTransfer/upload/core/uploadManager/config.ts
|
|
147
|
+
/**
|
|
148
|
+
* Configuration constants shared by the upload managers
|
|
149
|
+
*/
|
|
150
|
+
var CONFIG = Object.freeze({
|
|
151
|
+
/** Size, in bytes, of each uploaded file part. */
|
|
152
|
+
CHUNK_SIZE: 50 * 1024 * 1024,
|
|
153
|
+
/** Max number of retries for a failed request (part-url resolution or the chunk upload itself). */
|
|
154
|
+
DEFAULT_RETRY_COUNT: 5,
|
|
155
|
+
/** Delay, in ms, between retries. */
|
|
156
|
+
DEFAULT_RETRY_DELAY_MS: 50
|
|
157
|
+
});
|
|
158
|
+
//#endregion
|
|
159
|
+
//#region src/dataTransfer/upload/core/uploadManager/state.ts
|
|
160
|
+
var createStateManager = () => {
|
|
161
|
+
const uploadStateMap = /* @__PURE__ */ new Map();
|
|
162
|
+
const completedUploads = /* @__PURE__ */ new Set();
|
|
163
|
+
const cancelledUploads = /* @__PURE__ */ new Set();
|
|
164
|
+
const failedUploads = /* @__PURE__ */ new Set();
|
|
165
|
+
const assertState = (uploadId, state) => uploadStateMap.has(uploadId) && uploadStateMap.get(uploadId).state === state;
|
|
166
|
+
const getUploadState = (uploadId) => uploadStateMap.get(uploadId);
|
|
167
|
+
const setUploadState = (uploadId, state) => {
|
|
168
|
+
uploadStateMap.set(uploadId, {
|
|
169
|
+
...uploadStateMap.get(uploadId),
|
|
170
|
+
...state
|
|
171
|
+
});
|
|
172
|
+
};
|
|
173
|
+
const isFailed = (uploadId) => assertState(uploadId, "failed");
|
|
174
|
+
const isCancelled = (uploadId) => assertState(uploadId, "cancelled");
|
|
175
|
+
const abortInFlightUpload = (uploadId, reason) => {
|
|
176
|
+
const currentState = uploadStateMap.get(uploadId);
|
|
177
|
+
if (!currentState) return;
|
|
178
|
+
const message = currentState.fileDescriptor ? `Upload (${currentState.uploadId}) for file "${currentState.fileDescriptor.name}" aborted. Reason: "${reason}"` : `Upload (${currentState.uploadId}) aborted. Reason: "${reason}"`;
|
|
179
|
+
currentState.abortController.abort(new promise_exports.AbortError(message));
|
|
180
|
+
};
|
|
181
|
+
const cancel = (uploadId) => {
|
|
182
|
+
if (!uploadStateMap.has(uploadId)) {
|
|
183
|
+
consola.error("cancel:: Invalid upload id");
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
setUploadState(uploadId, { state: "cancelled" });
|
|
187
|
+
abortInFlightUpload(uploadId, "upload cancelled");
|
|
188
|
+
cancelledUploads.add(uploadId);
|
|
189
|
+
};
|
|
190
|
+
const cancelAll = () => {
|
|
191
|
+
for (const [id, upload] of uploadStateMap.entries()) if (upload.state === "inprogress") cancel(id);
|
|
192
|
+
};
|
|
193
|
+
const setCompleted = (uploadId, completedChunkCount) => {
|
|
194
|
+
setUploadState(uploadId, {
|
|
195
|
+
completedChunkCount,
|
|
196
|
+
state: "complete"
|
|
197
|
+
});
|
|
198
|
+
completedUploads.add(uploadId);
|
|
199
|
+
};
|
|
200
|
+
const setFailed = (uploadId) => {
|
|
201
|
+
if (!uploadStateMap.has(uploadId)) {
|
|
202
|
+
consola.error("failed:: Invalid upload id");
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
setUploadState(uploadId, { state: "failed" });
|
|
206
|
+
failedUploads.add(uploadId);
|
|
207
|
+
if (completedUploads.has(uploadId)) completedUploads.delete(uploadId);
|
|
208
|
+
};
|
|
209
|
+
const flush = () => {
|
|
210
|
+
const completed = [...completedUploads.values()];
|
|
211
|
+
const cancelled = [...cancelledUploads.values()];
|
|
212
|
+
const failed = [...failedUploads.values()];
|
|
213
|
+
[
|
|
214
|
+
...completed,
|
|
215
|
+
...cancelled,
|
|
216
|
+
...failed
|
|
217
|
+
].forEach((uploadId) => uploadStateMap.delete(uploadId));
|
|
218
|
+
completedUploads.clear();
|
|
219
|
+
cancelledUploads.clear();
|
|
220
|
+
failedUploads.clear();
|
|
221
|
+
return {
|
|
222
|
+
completed,
|
|
223
|
+
cancelled,
|
|
224
|
+
failed
|
|
225
|
+
};
|
|
226
|
+
};
|
|
227
|
+
return {
|
|
228
|
+
get: getUploadState,
|
|
229
|
+
set: setUploadState,
|
|
230
|
+
isFailed,
|
|
231
|
+
isCancelled,
|
|
232
|
+
cancel,
|
|
233
|
+
cancelAll,
|
|
234
|
+
setCompleted,
|
|
235
|
+
setFailed,
|
|
236
|
+
abortInFlightUpload,
|
|
237
|
+
flush
|
|
238
|
+
};
|
|
239
|
+
};
|
|
240
|
+
//#endregion
|
|
241
|
+
//#region src/dataTransfer/upload/core/uploadManager/uploadPartBytes.ts
|
|
242
|
+
/**
|
|
243
|
+
* Uploads a single file part via XHR, reporting upload progress and
|
|
244
|
+
* resolving with the part's identifying ETag (or an empty string if the
|
|
245
|
+
* backend doesn't return one).
|
|
246
|
+
*/
|
|
247
|
+
var uploadPartBytesWithProgress = (params) => {
|
|
248
|
+
const { method, url, partBytes, partIndex, onProgress, abortSignal } = params;
|
|
249
|
+
const OK = 200;
|
|
250
|
+
const CREATED = 201;
|
|
251
|
+
return new Promise((resolve, reject) => {
|
|
252
|
+
const xhr = new XMLHttpRequest();
|
|
253
|
+
xhr.open(method, url, true);
|
|
254
|
+
abortSignal.addEventListener("abort", () => xhr.abort());
|
|
255
|
+
xhr.upload.onprogress = (event) => {
|
|
256
|
+
if (event.lengthComputable) onProgress(event.loaded);
|
|
257
|
+
};
|
|
258
|
+
xhr.onload = () => {
|
|
259
|
+
if (xhr.status === OK || xhr.status === CREATED) resolve({ partId: xhr.getResponseHeader("ETag")?.replace(/^(?:W\/)?"|"$/g, "") ?? "" });
|
|
260
|
+
else {
|
|
261
|
+
consola.error("Failed or unexpected XHR response", {
|
|
262
|
+
xhrStatus: xhr.status,
|
|
263
|
+
xhrResponse: xhr.response,
|
|
264
|
+
partIndex
|
|
265
|
+
});
|
|
266
|
+
reject(/* @__PURE__ */ new Error("Failed to upload file"));
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
xhr.onerror = (event) => {
|
|
270
|
+
consola.error("Failed to upload part", {
|
|
271
|
+
xhrErrorEvent: event,
|
|
272
|
+
partIndex
|
|
273
|
+
});
|
|
274
|
+
reject(/* @__PURE__ */ new Error("Failed to upload file"));
|
|
275
|
+
};
|
|
276
|
+
xhr.send(partBytes);
|
|
277
|
+
});
|
|
278
|
+
};
|
|
279
|
+
//#endregion
|
|
280
|
+
//#region src/dataTransfer/upload/core/uploadManager/chunkedUploadManager.ts
|
|
281
|
+
/**
|
|
282
|
+
* Creates and returns an `uploadManager`, a comprehensive multi-file, multi-part upload handler.
|
|
283
|
+
*
|
|
284
|
+
* The `uploadManager` offers functionality for handling file uploads in parts and provides
|
|
285
|
+
* several built-in mechanisms to ensure the process is reliable and adaptable.
|
|
286
|
+
*
|
|
287
|
+
* Key features include:
|
|
288
|
+
*
|
|
289
|
+
* - **Chunked File Uploads**: Splits provided files into manageable chunks and initiates the upload
|
|
290
|
+
* for each part using the `resolveFilePartUploadURL` function provided in the configuration.
|
|
291
|
+
*
|
|
292
|
+
* - **Retry Mechanism**: Automatically retries failed upload attempts up to a maximum of 5 times
|
|
293
|
+
* per failed request.
|
|
294
|
+
*
|
|
295
|
+
* - **Upload Cancellation**: Allows cancellation of ongoing uploads, giving users control over the process.
|
|
296
|
+
*
|
|
297
|
+
* - **Progress Tracking**: Monitors the progress of each file part upload and triggers the `onProgress`
|
|
298
|
+
* callback to provide updates.
|
|
299
|
+
*
|
|
300
|
+
* - **Error Handling**: The returned `uploadFiles` function does not throw errors directly. Instead,
|
|
301
|
+
* it manages retries and stops any failed uploads after the retry limit is reached, marking them as failed.
|
|
302
|
+
* This ensures that the promise it returns completes gracefully without uncaught exceptions.
|
|
303
|
+
*
|
|
304
|
+
* This is the "slice-based" manager: it needs each file's complete bytes to already be
|
|
305
|
+
* available (as a real `File`/`Blob` with a known `.size`), since it produces each part via
|
|
306
|
+
* `file.slice()`. For uploading from a source whose total size isn't known upfront (e.g. a
|
|
307
|
+
* stream of compressed output), see `createStreamingUploadManager` instead.
|
|
308
|
+
*/
|
|
309
|
+
var createUploadManager = (config) => {
|
|
310
|
+
const { resolveFilePartUploadURL: getUploadFilePartUrl, onFileUploadComplete, onFileUploadFailed } = config;
|
|
311
|
+
const state = createStateManager();
|
|
312
|
+
const getTotalFileParts = (file) => Math.ceil(file.size / CONFIG.CHUNK_SIZE);
|
|
313
|
+
const uploadFileFromChunkIndex = async (uploadId, file) => {
|
|
314
|
+
const totalChunks = getTotalFileParts(file);
|
|
315
|
+
const totalBytes = file.size;
|
|
316
|
+
const completedPartIds = {};
|
|
317
|
+
const { abortController, runAbortablePromise } = promise_exports.createAbortablePromise();
|
|
318
|
+
state.set(uploadId, {
|
|
319
|
+
uploadId,
|
|
320
|
+
fileDescriptor: {
|
|
321
|
+
name: file.name,
|
|
322
|
+
type: file.type
|
|
323
|
+
},
|
|
324
|
+
state: "inprogress",
|
|
325
|
+
uploadedByteCount: state.get(uploadId)?.uploadedByteCount ?? 0,
|
|
326
|
+
completedChunkCount: state.get(uploadId)?.completedChunkCount ?? 0,
|
|
327
|
+
abortController
|
|
328
|
+
});
|
|
329
|
+
let chunkIndex = 0;
|
|
330
|
+
let uploadedBytes = state.get(uploadId)?.uploadedByteCount;
|
|
331
|
+
while (chunkIndex < totalChunks) {
|
|
332
|
+
if (state.isCancelled(uploadId) || state.isFailed(uploadId)) break;
|
|
333
|
+
const start = chunkIndex * CONFIG.CHUNK_SIZE;
|
|
334
|
+
const end = Math.min(start + CONFIG.CHUNK_SIZE, file.size);
|
|
335
|
+
const chunk = file.slice(start, end);
|
|
336
|
+
try {
|
|
337
|
+
const { method, url } = await promise_exports.retryPromise({
|
|
338
|
+
fn: () => getUploadFilePartUrl(uploadId, chunkIndex + 1),
|
|
339
|
+
retryCount: CONFIG.DEFAULT_RETRY_COUNT,
|
|
340
|
+
retryDelayMS: CONFIG.DEFAULT_RETRY_DELAY_MS
|
|
341
|
+
});
|
|
342
|
+
consola.trace("File part upload url resolved", {
|
|
343
|
+
method,
|
|
344
|
+
url
|
|
345
|
+
});
|
|
346
|
+
const onChunkUploadProgress = (uploadedChunkBytes) => {
|
|
347
|
+
uploadedBytes = uploadedChunkBytes + start;
|
|
348
|
+
state.set(uploadId, { uploadedByteCount: uploadedBytes });
|
|
349
|
+
const progress = Math.round(uploadedBytes / totalBytes * 100);
|
|
350
|
+
config.onProgress?.(uploadId, progress);
|
|
351
|
+
};
|
|
352
|
+
const filePartResponse = await runAbortablePromise(() => promise_exports.retryPromise({
|
|
353
|
+
fn: () => uploadPartBytesWithProgress({
|
|
354
|
+
method,
|
|
355
|
+
url,
|
|
356
|
+
partBytes: chunk,
|
|
357
|
+
partIndex: chunkIndex,
|
|
358
|
+
onProgress: onChunkUploadProgress,
|
|
359
|
+
abortSignal: abortController.signal
|
|
360
|
+
}),
|
|
361
|
+
retryCount: CONFIG.DEFAULT_RETRY_COUNT,
|
|
362
|
+
retryDelayMS: CONFIG.DEFAULT_RETRY_DELAY_MS
|
|
363
|
+
}));
|
|
364
|
+
consola.trace(`${file.name}: Chunk ${chunkIndex + 1} of ${totalChunks} uploaded successfully`);
|
|
365
|
+
completedPartIds[chunkIndex + 1] = filePartResponse.partId;
|
|
366
|
+
state.set(uploadId, { completedChunkCount: state.get(uploadId).completedChunkCount + 1 });
|
|
367
|
+
} catch (_error) {
|
|
368
|
+
if (_error instanceof promise_exports.AbortError) {
|
|
369
|
+
consola.info("Aborted::", _error);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
const error = _error instanceof Error ? _error : new Error(_error);
|
|
373
|
+
state.setFailed(uploadId);
|
|
374
|
+
onFileUploadFailed?.({
|
|
375
|
+
uploadId,
|
|
376
|
+
error
|
|
377
|
+
});
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
chunkIndex++;
|
|
381
|
+
}
|
|
382
|
+
if (state.isCancelled(uploadId) || state.isFailed(uploadId)) return;
|
|
383
|
+
state.setCompleted(uploadId, totalChunks);
|
|
384
|
+
onFileUploadComplete?.({
|
|
385
|
+
uploadId,
|
|
386
|
+
filePartIds: completedPartIds
|
|
387
|
+
});
|
|
388
|
+
};
|
|
389
|
+
const uploadFiles = (files) => {
|
|
390
|
+
const result = [];
|
|
391
|
+
for (const { uploadId, file } of files) result.push(uploadFileFromChunkIndex(uploadId, file));
|
|
392
|
+
return Promise.allSettled(result).then(() => state.flush());
|
|
393
|
+
};
|
|
394
|
+
return {
|
|
395
|
+
uploadFiles,
|
|
396
|
+
getUploadState: state.get,
|
|
397
|
+
isFailed: state.isFailed,
|
|
398
|
+
isCancelled: state.isCancelled,
|
|
399
|
+
cancel: state.cancel,
|
|
400
|
+
cancelAll: state.cancelAll,
|
|
401
|
+
setFailed: state.setFailed
|
|
402
|
+
};
|
|
403
|
+
};
|
|
404
|
+
//#endregion
|
|
405
|
+
//#region src/dataTransfer/upload/core/uploadManager/streamingUploadManager.ts
|
|
406
|
+
/**
|
|
407
|
+
* A small FIFO byte buffer. Pushed chunks are stored by reference (no
|
|
408
|
+
* copying on push).
|
|
409
|
+
*/
|
|
410
|
+
var ByteQueue = class {
|
|
411
|
+
#chunks = [];
|
|
412
|
+
#length = 0;
|
|
413
|
+
get length() {
|
|
414
|
+
return this.#length;
|
|
415
|
+
}
|
|
416
|
+
push(chunk) {
|
|
417
|
+
if (chunk.length === 0) return;
|
|
418
|
+
this.#chunks.push(chunk);
|
|
419
|
+
this.#length += chunk.length;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* FIFO exactly `n` bytes from the buffer. If the first chunk has more bytes than needed,
|
|
423
|
+
* it will be split and only the requested amount returned. This ensures each byte is copied
|
|
424
|
+
* at most once, regardless of how many small chunks it took to accumulate them.
|
|
425
|
+
* @param n
|
|
426
|
+
* @returns
|
|
427
|
+
*/
|
|
428
|
+
take(n) {
|
|
429
|
+
const size = Math.min(n, this.#length);
|
|
430
|
+
const result = new Uint8Array(size);
|
|
431
|
+
let offset = 0;
|
|
432
|
+
while (offset < size) {
|
|
433
|
+
const chunk = this.#chunks[0];
|
|
434
|
+
const needed = size - offset;
|
|
435
|
+
if (chunk.length <= needed) {
|
|
436
|
+
result.set(chunk, offset);
|
|
437
|
+
offset += chunk.length;
|
|
438
|
+
this.#chunks.shift();
|
|
439
|
+
} else {
|
|
440
|
+
result.set(chunk.subarray(0, needed), offset);
|
|
441
|
+
this.#chunks[0] = chunk.subarray(needed);
|
|
442
|
+
offset += needed;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
this.#length -= size;
|
|
446
|
+
return result;
|
|
447
|
+
}
|
|
448
|
+
takeAll() {
|
|
449
|
+
return this.take(this.#length);
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
var readStreamAndUpload = async (state, uploadId, stream, uploadPart, config) => {
|
|
453
|
+
const buffer = new ByteQueue();
|
|
454
|
+
const reader = stream.getReader();
|
|
455
|
+
const closeReader = async () => {
|
|
456
|
+
try {
|
|
457
|
+
await reader.cancel();
|
|
458
|
+
} catch {} finally {
|
|
459
|
+
reader.releaseLock();
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
try {
|
|
463
|
+
while (true) {
|
|
464
|
+
if (state.isCancelled(uploadId) || state.isFailed(uploadId)) return;
|
|
465
|
+
const { value, done } = await reader.read();
|
|
466
|
+
if (value) buffer.push(value);
|
|
467
|
+
while (buffer.length >= CONFIG.CHUNK_SIZE) {
|
|
468
|
+
if (state.isCancelled(uploadId) || state.isFailed(uploadId)) return;
|
|
469
|
+
await uploadPart(buffer.take(CONFIG.CHUNK_SIZE));
|
|
470
|
+
}
|
|
471
|
+
if (done) {
|
|
472
|
+
if (buffer.length > 0 && !state.isCancelled(uploadId) && !state.isFailed(uploadId)) await uploadPart(buffer.takeAll());
|
|
473
|
+
break;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
} catch (_error) {
|
|
477
|
+
if (_error instanceof promise_exports.AbortError) {
|
|
478
|
+
consola.info("Aborted::", _error);
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
state.setFailed(uploadId);
|
|
482
|
+
const error = _error instanceof Error ? _error : new Error(_error);
|
|
483
|
+
config.onFileUploadFailed?.({
|
|
484
|
+
uploadId,
|
|
485
|
+
error
|
|
486
|
+
});
|
|
487
|
+
} finally {
|
|
488
|
+
await closeReader();
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
/**
|
|
492
|
+
* Creates and returns a `streamingUploadManager` - a multi-part upload handler
|
|
493
|
+
* driven by a `ReadableStream<Uint8Array>` rather than a `File`/`Blob`.
|
|
494
|
+
*
|
|
495
|
+
* Unlike the chunkedUploadManager, this doesn't need to know the total size of the upload upfront.
|
|
496
|
+
* Instead, it accumulates stream chunks into a buffer and uploadsa part every time
|
|
497
|
+
* the buffer crosses the configured threshold for upload chunks, flushing whatever remains
|
|
498
|
+
* as a final (possibly smaller) part once the stream ends.
|
|
499
|
+
*
|
|
500
|
+
* This bounds memory to roughly one chunk's worth of buffered bytes, plus
|
|
501
|
+
* whatever the stream source itself buffers, regardless of the eventual
|
|
502
|
+
* total size - useful for sources whose size can't be known in advance,
|
|
503
|
+
* such as compressed output from a streaming zip encoder.
|
|
504
|
+
*
|
|
505
|
+
* Shares the same part-upload mechanics (retries, progress, abort) as
|
|
506
|
+
* the chunkedUploadManager
|
|
507
|
+
*/
|
|
508
|
+
var createStreamingUploadManager = (config) => {
|
|
509
|
+
const { resolveFilePartUploadURL: getUploadFilePartUrl, onFileUploadComplete } = config;
|
|
510
|
+
const state = createStateManager();
|
|
511
|
+
const uploadStreamFromPartIndex = async (uploadId, stream) => {
|
|
512
|
+
const completedPartIds = {};
|
|
513
|
+
const { abortController, runAbortablePromise } = promise_exports.createAbortablePromise();
|
|
514
|
+
state.set(uploadId, {
|
|
515
|
+
uploadId,
|
|
516
|
+
state: "inprogress",
|
|
517
|
+
uploadedByteCount: state.get(uploadId)?.uploadedByteCount ?? 0,
|
|
518
|
+
completedChunkCount: state.get(uploadId)?.completedChunkCount ?? 0,
|
|
519
|
+
abortController
|
|
520
|
+
});
|
|
521
|
+
let partIndex = 0;
|
|
522
|
+
let uploadedBytes = state.get(uploadId)?.uploadedByteCount ?? 0;
|
|
523
|
+
const uploadPart = async (bytes) => {
|
|
524
|
+
const chunk = new Blob([bytes]);
|
|
525
|
+
const { method, url } = await promise_exports.retryPromise({
|
|
526
|
+
fn: () => getUploadFilePartUrl(uploadId, partIndex + 1),
|
|
527
|
+
retryCount: CONFIG.DEFAULT_RETRY_COUNT,
|
|
528
|
+
retryDelayMS: CONFIG.DEFAULT_RETRY_DELAY_MS
|
|
529
|
+
});
|
|
530
|
+
const uploadedBeforeThisPart = uploadedBytes;
|
|
531
|
+
const onChunkUploadProgress = (uploadedChunkBytes) => {
|
|
532
|
+
uploadedBytes = uploadedBeforeThisPart + uploadedChunkBytes;
|
|
533
|
+
state.set(uploadId, { uploadedByteCount: uploadedBytes });
|
|
534
|
+
config.onProgress?.(uploadId, uploadedBytes);
|
|
535
|
+
};
|
|
536
|
+
const filePartResponse = await runAbortablePromise(() => promise_exports.retryPromise({
|
|
537
|
+
fn: () => uploadPartBytesWithProgress({
|
|
538
|
+
method,
|
|
539
|
+
url,
|
|
540
|
+
partBytes: chunk,
|
|
541
|
+
partIndex,
|
|
542
|
+
onProgress: onChunkUploadProgress,
|
|
543
|
+
abortSignal: abortController.signal
|
|
544
|
+
}),
|
|
545
|
+
retryCount: CONFIG.DEFAULT_RETRY_COUNT,
|
|
546
|
+
retryDelayMS: CONFIG.DEFAULT_RETRY_DELAY_MS
|
|
547
|
+
}));
|
|
548
|
+
completedPartIds[partIndex + 1] = filePartResponse.partId;
|
|
549
|
+
uploadedBytes = uploadedBeforeThisPart + bytes.length;
|
|
550
|
+
partIndex++;
|
|
551
|
+
state.set(uploadId, {
|
|
552
|
+
completedChunkCount: partIndex,
|
|
553
|
+
uploadedByteCount: uploadedBytes
|
|
554
|
+
});
|
|
555
|
+
};
|
|
556
|
+
await readStreamAndUpload(state, uploadId, stream, uploadPart, config);
|
|
557
|
+
if (state.isCancelled(uploadId) || state.isFailed(uploadId)) return;
|
|
558
|
+
state.setCompleted(uploadId, state.get(uploadId).completedChunkCount);
|
|
559
|
+
onFileUploadComplete?.({
|
|
560
|
+
uploadId,
|
|
561
|
+
filePartIds: completedPartIds
|
|
562
|
+
});
|
|
563
|
+
};
|
|
564
|
+
const uploadStreams = (streams) => {
|
|
565
|
+
const result = [];
|
|
566
|
+
for (const { uploadId, stream } of streams) result.push(uploadStreamFromPartIndex(uploadId, stream));
|
|
567
|
+
return Promise.allSettled(result).then(() => state.flush());
|
|
568
|
+
};
|
|
569
|
+
return {
|
|
570
|
+
uploadStreams,
|
|
571
|
+
getUploadState: state.get,
|
|
572
|
+
isFailed: state.isFailed,
|
|
573
|
+
isCancelled: state.isCancelled,
|
|
574
|
+
cancel: state.cancel,
|
|
575
|
+
cancelAll: state.cancelAll,
|
|
576
|
+
setFailed: state.setFailed
|
|
577
|
+
};
|
|
578
|
+
};
|
|
579
|
+
//#endregion
|
|
580
|
+
//#region src/dataTransfer/upload/core/uploadManager/types.ts
|
|
581
|
+
var UploadStates = {
|
|
582
|
+
IDLE: "idle",
|
|
583
|
+
IN_PROGRESS: "inprogress",
|
|
584
|
+
CANCELLED: "cancelled",
|
|
585
|
+
FAILED: "failed",
|
|
586
|
+
COMPLETE: "complete"
|
|
587
|
+
};
|
|
588
|
+
//#endregion
|
|
589
|
+
//#region src/dataTransfer/upload/core/uploadClient/uploadClient.ts
|
|
590
|
+
/**
|
|
591
|
+
* Creates the raw upload lifecycle client:
|
|
592
|
+
*
|
|
593
|
+
* (1) - prepare a batch of items for upload
|
|
594
|
+
* (2) - resolve a presigned URL for a given file part
|
|
595
|
+
* (3) - complete an upload once all its parts are uploaded, or cancel one in progress.
|
|
596
|
+
*/
|
|
597
|
+
var createUploadClient = (options = {}) => {
|
|
598
|
+
const $ofetch = createHttpClient(options.customFetchClientOptions);
|
|
599
|
+
const prepareUpload = (parentId, items) => {
|
|
600
|
+
const fileByName = new Map(items.map(({ path: name, file }) => [name, file]));
|
|
601
|
+
const body = { items: Object.fromEntries(items.map(({ path: name, file }) => [name, {
|
|
602
|
+
itemContentType: getFileMimeType(file),
|
|
603
|
+
itemContentSize: file.size
|
|
604
|
+
}])) };
|
|
605
|
+
return $ofetch(`/repository/${parentId}/manifest`, {
|
|
606
|
+
method: "POST",
|
|
607
|
+
body
|
|
608
|
+
}).then(({ items: responseItems }) => Object.keys(responseItems).map((name) => ({
|
|
609
|
+
uploadId: responseItems[name].uploadId,
|
|
610
|
+
file: fileByName.get(name),
|
|
611
|
+
path: name
|
|
612
|
+
})));
|
|
613
|
+
};
|
|
614
|
+
const resolveFilePartUploadURL = (uploadId, partNumber) => {
|
|
615
|
+
return $ofetch(`/uploads/${uploadId}/parts/?partNumber=${partNumber}`, { method: "POST" });
|
|
616
|
+
};
|
|
617
|
+
const completeUpload = (uploadId, partIds) => {
|
|
618
|
+
return $ofetch(`/uploads/${uploadId}`, {
|
|
619
|
+
method: "POST",
|
|
620
|
+
body: partIds
|
|
621
|
+
});
|
|
622
|
+
};
|
|
623
|
+
const cancelUpload = (uploadId) => {
|
|
624
|
+
return $ofetch(`/uploads/${uploadId}`, { method: "DELETE" });
|
|
625
|
+
};
|
|
626
|
+
const getUploadStatus = (uploadIds) => {
|
|
627
|
+
return $ofetch("/uploads/status", {
|
|
628
|
+
method: "GET",
|
|
629
|
+
query: { uploadIds: uploadIds.join(",") }
|
|
630
|
+
});
|
|
631
|
+
};
|
|
632
|
+
return {
|
|
633
|
+
prepareUpload,
|
|
634
|
+
resolveFilePartUploadURL,
|
|
635
|
+
completeUpload,
|
|
636
|
+
cancelUpload,
|
|
637
|
+
getUploadStatus
|
|
638
|
+
};
|
|
639
|
+
};
|
|
640
|
+
//#endregion
|
|
641
|
+
//#region ../../node_modules/.pnpm/es-toolkit@1.49.0/node_modules/es-toolkit/dist/array/chunk.mjs
|
|
642
|
+
/**
|
|
643
|
+
* Splits an array into smaller arrays of a specified length.
|
|
644
|
+
*
|
|
645
|
+
* This function takes an input array and divides it into multiple smaller arrays,
|
|
646
|
+
* each of a specified length. If the input array cannot be evenly divided,
|
|
647
|
+
* the final sub-array will contain the remaining elements.
|
|
648
|
+
*
|
|
649
|
+
* @template T The type of elements in the array.
|
|
650
|
+
* @param arr - The array to be chunked into smaller arrays.
|
|
651
|
+
* @param size - The size of each smaller array. Must be a positive integer.
|
|
652
|
+
* @returns A two-dimensional array where each sub-array has a maximum length of `size`.
|
|
653
|
+
* @throws {Error} Throws an error if `size` is not a positive integer.
|
|
654
|
+
*
|
|
655
|
+
* @example
|
|
656
|
+
* // Splits an array of numbers into sub-arrays of length 2
|
|
657
|
+
* chunk([1, 2, 3, 4, 5], 2);
|
|
658
|
+
* // Returns: [[1, 2], [3, 4], [5]]
|
|
659
|
+
*
|
|
660
|
+
* @example
|
|
661
|
+
* // Splits an array of strings into sub-arrays of length 3
|
|
662
|
+
* chunk(['a', 'b', 'c', 'd', 'e', 'f', 'g'], 3);
|
|
663
|
+
* // Returns: [['a', 'b', 'c'], ['d', 'e', 'f'], ['g']]
|
|
664
|
+
*/
|
|
665
|
+
function chunk(arr, size) {
|
|
666
|
+
if (!Number.isInteger(size) || size <= 0) throw new Error("Size must be an integer greater than zero.");
|
|
667
|
+
const chunkLength = Math.ceil(arr.length / size);
|
|
668
|
+
const result = Array(chunkLength);
|
|
669
|
+
for (let index = 0; index < chunkLength; index++) {
|
|
670
|
+
const start = index * size;
|
|
671
|
+
const end = start + size;
|
|
672
|
+
result[index] = arr.slice(start, end);
|
|
673
|
+
}
|
|
674
|
+
return result;
|
|
675
|
+
}
|
|
676
|
+
//#endregion
|
|
677
|
+
//#region src/dataTransfer/upload/core/uploadClient/createUploadStatusPoller.ts
|
|
678
|
+
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
679
|
+
var DEFAULT_POLL_TIMEOUT = 300 * 1e3;
|
|
680
|
+
var MAX_IDS_PER_REQUEST = 100;
|
|
681
|
+
var logger = () => consola.withTag("Upload status poller");
|
|
682
|
+
function createUploadStatusPoller(uploadClient, options) {
|
|
683
|
+
const pending = /* @__PURE__ */ new Set();
|
|
684
|
+
const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
685
|
+
const pollTimeout = options.pollTimeout ?? DEFAULT_POLL_TIMEOUT;
|
|
686
|
+
let inFlight = null;
|
|
687
|
+
const poll = () => {
|
|
688
|
+
if (inFlight) return inFlight;
|
|
689
|
+
inFlight = (async () => {
|
|
690
|
+
const deadline = Date.now() + pollTimeout;
|
|
691
|
+
while (pending.size > 0 && Date.now() < deadline) try {
|
|
692
|
+
const promises = chunk([...pending.values()], MAX_IDS_PER_REQUEST).map((batch) => uploadClient.getUploadStatus(batch));
|
|
693
|
+
const statuses = (await Promise.all(promises)).flat();
|
|
694
|
+
for (const { uploadId, status, statusMessage } of statuses) if (status === "COMPLETED") {
|
|
695
|
+
pending.delete(uploadId);
|
|
696
|
+
options.onComplete(uploadId);
|
|
697
|
+
} else if (status === "FAILED" || status === "ABORTED") {
|
|
698
|
+
pending.delete(uploadId);
|
|
699
|
+
options.onFail(uploadId, statusMessage);
|
|
700
|
+
}
|
|
701
|
+
if (pending.size === 0) break;
|
|
702
|
+
await sleep(pollIntervalMs);
|
|
703
|
+
} catch (error) {
|
|
704
|
+
logger().error("Failed while polling for upload batch status", { error });
|
|
705
|
+
await sleep(pollIntervalMs);
|
|
706
|
+
continue;
|
|
707
|
+
}
|
|
708
|
+
for (const uploadId of pending) {
|
|
709
|
+
pending.delete(uploadId);
|
|
710
|
+
options.onFail(uploadId, "Timed out waiting for processing to complete");
|
|
711
|
+
}
|
|
712
|
+
})().finally(() => {
|
|
713
|
+
inFlight = null;
|
|
714
|
+
});
|
|
715
|
+
return inFlight;
|
|
716
|
+
};
|
|
717
|
+
return {
|
|
718
|
+
/**
|
|
719
|
+
* Tracks an id for polling. Will start the polling requests immediately unless indicated otherwise
|
|
720
|
+
* by the `startPolling` parameter.
|
|
721
|
+
*
|
|
722
|
+
* Adding newer ids to an already-running poller is not a problem, since new ids just
|
|
723
|
+
* increase the poll batch size upon the next execution of the polling request
|
|
724
|
+
*
|
|
725
|
+
* @param uploadId
|
|
726
|
+
* @param startPolling
|
|
727
|
+
*/
|
|
728
|
+
track: (uploadId, startPolling = true) => {
|
|
729
|
+
pending.add(uploadId);
|
|
730
|
+
if (startPolling) return poll();
|
|
731
|
+
return Promise.resolve();
|
|
732
|
+
},
|
|
733
|
+
stop: () => pending.clear()
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
//#endregion
|
|
737
|
+
export { createUploadManager as a, API_BASE_URL as c, createStreamingUploadManager as i, createUploadClient as n, createHttpClient as o, UploadStates as r, defaultConfig as s, createUploadStatusPoller as t };
|
|
738
|
+
|
|
739
|
+
//# sourceMappingURL=core-CVWznjtP.js.map
|