UNPKG

@uploadx/client

Version:

Resumable upload client for browser and Node.js

934 lines (912 loc) 47.2 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('axios')) : typeof define === 'function' && define.amd ? define(['axios'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.UploadxClient = factory(global.axios)); })(this, (function (axios) { 'use strict'; /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __awaiter(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } function __generator(thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } } typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; function getDefaultExportFromCjs (x) { return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; } var isRetryAllowed$1; var hasRequiredIsRetryAllowed; function requireIsRetryAllowed () { if (hasRequiredIsRetryAllowed) return isRetryAllowed$1; hasRequiredIsRetryAllowed = 1; const denyList = new Set([ 'ENOTFOUND', 'ENETUNREACH', // SSL errors from https://github.com/nodejs/node/blob/fc8e3e2cdc521978351de257030db0076d79e0ab/src/crypto/crypto_common.cc#L301-L328 'UNABLE_TO_GET_ISSUER_CERT', 'UNABLE_TO_GET_CRL', 'UNABLE_TO_DECRYPT_CERT_SIGNATURE', 'UNABLE_TO_DECRYPT_CRL_SIGNATURE', 'UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY', 'CERT_SIGNATURE_FAILURE', 'CRL_SIGNATURE_FAILURE', 'CERT_NOT_YET_VALID', 'CERT_HAS_EXPIRED', 'CRL_NOT_YET_VALID', 'CRL_HAS_EXPIRED', 'ERROR_IN_CERT_NOT_BEFORE_FIELD', 'ERROR_IN_CERT_NOT_AFTER_FIELD', 'ERROR_IN_CRL_LAST_UPDATE_FIELD', 'ERROR_IN_CRL_NEXT_UPDATE_FIELD', 'OUT_OF_MEM', 'DEPTH_ZERO_SELF_SIGNED_CERT', 'SELF_SIGNED_CERT_IN_CHAIN', 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY', 'UNABLE_TO_VERIFY_LEAF_SIGNATURE', 'CERT_CHAIN_TOO_LONG', 'CERT_REVOKED', 'INVALID_CA', 'PATH_LENGTH_EXCEEDED', 'INVALID_PURPOSE', 'CERT_UNTRUSTED', 'CERT_REJECTED', 'HOSTNAME_MISMATCH' ]); // TODO: Use `error?.code` when targeting Node.js 14 isRetryAllowed$1 = error => !denyList.has(error && error.code); return isRetryAllowed$1; } var isRetryAllowedExports = requireIsRetryAllowed(); var isRetryAllowed = /*@__PURE__*/getDefaultExportFromCjs(isRetryAllowedExports); const namespace = 'axios-retry'; function isNetworkError(error) { const CODE_EXCLUDE_LIST = ['ERR_CANCELED', 'ECONNABORTED']; if (error.response) { return false; } if (!error.code) { return false; } // Prevents retrying timed out & cancelled requests if (CODE_EXCLUDE_LIST.includes(error.code)) { return false; } // Prevents retrying unsafe errors return isRetryAllowed(error); } const SAFE_HTTP_METHODS = ['get', 'head', 'options']; const IDEMPOTENT_HTTP_METHODS = SAFE_HTTP_METHODS.concat(['put', 'delete']); function isRetryableError(error) { return (error.code !== 'ECONNABORTED' && (!error.response || error.response.status === 429 || (error.response.status >= 500 && error.response.status <= 599))); } function isSafeRequestError(error) { if (!error.config?.method) { // Cannot determine if the request can be retried return false; } return isRetryableError(error) && SAFE_HTTP_METHODS.indexOf(error.config.method) !== -1; } function isIdempotentRequestError(error) { if (!error.config?.method) { // Cannot determine if the request can be retried return false; } return isRetryableError(error) && IDEMPOTENT_HTTP_METHODS.indexOf(error.config.method) !== -1; } function isNetworkOrIdempotentRequestError(error) { return isNetworkError(error) || isIdempotentRequestError(error); } function retryAfter(error = undefined) { const retryAfterHeader = error?.response?.headers['retry-after']; if (!retryAfterHeader) { return 0; } // if the retry after header is a number, convert it to milliseconds let retryAfterMs = (Number(retryAfterHeader) || 0) * 1000; // If the retry after header is a date, get the number of milliseconds until that date if (retryAfterMs === 0) { retryAfterMs = (new Date(retryAfterHeader).valueOf() || 0) - Date.now(); } return Math.max(0, retryAfterMs); } function noDelay(_retryNumber = 0, error = undefined) { return Math.max(0, retryAfter(error)); } function exponentialDelay(retryNumber = 0, error = undefined, delayFactor = 100) { const calculatedDelay = 2 ** retryNumber * delayFactor; const delay = Math.max(calculatedDelay, retryAfter(error)); const randomSum = delay * 0.2 * Math.random(); // 0-20% of the delay return delay + randomSum; } /** * Linear delay * @param {number | undefined} delayFactor - delay factor in milliseconds (default: 100) * @returns {function} (retryNumber: number, error: AxiosError | undefined) => number */ function linearDelay(delayFactor = 100) { return (retryNumber = 0, error = undefined) => { const delay = retryNumber * delayFactor; return Math.max(delay, retryAfter(error)); }; } const DEFAULT_OPTIONS = { retries: 3, retryCondition: isNetworkOrIdempotentRequestError, retryDelay: noDelay, shouldResetTimeout: false, onRetry: () => { }, onMaxRetryTimesExceeded: () => { }, validateResponse: null }; function getRequestOptions(config, defaultOptions) { return { ...DEFAULT_OPTIONS, ...defaultOptions, ...config[namespace] }; } function setCurrentState(config, defaultOptions, resetLastRequestTime = false) { const currentState = getRequestOptions(config, defaultOptions || {}); currentState.retryCount = currentState.retryCount || 0; if (!currentState.lastRequestTime || resetLastRequestTime) { currentState.lastRequestTime = Date.now(); } config[namespace] = currentState; return currentState; } function fixConfig(axiosInstance, config) { // @ts-ignore if (axiosInstance.defaults.agent === config.agent) { // @ts-ignore delete config.agent; } if (axiosInstance.defaults.httpAgent === config.httpAgent) { delete config.httpAgent; } if (axiosInstance.defaults.httpsAgent === config.httpsAgent) { delete config.httpsAgent; } } async function shouldRetry(currentState, error) { const { retries, retryCondition } = currentState; const shouldRetryOrPromise = (currentState.retryCount || 0) < retries && retryCondition(error); // This could be a promise if (typeof shouldRetryOrPromise === 'object') { try { const shouldRetryPromiseResult = await shouldRetryOrPromise; // keep return true unless shouldRetryPromiseResult return false for compatibility return shouldRetryPromiseResult !== false; } catch (_err) { return false; } } return shouldRetryOrPromise; } async function handleRetry(axiosInstance, currentState, error, config) { currentState.retryCount += 1; const { retryDelay, shouldResetTimeout, onRetry } = currentState; const delay = retryDelay(currentState.retryCount, error); // Axios fails merging this configuration to the default configuration because it has an issue // with circular structures: https://github.com/mzabriskie/axios/issues/370 fixConfig(axiosInstance, config); if (!shouldResetTimeout && config.timeout && currentState.lastRequestTime) { const lastRequestDuration = Date.now() - currentState.lastRequestTime; const timeout = config.timeout - lastRequestDuration - delay; if (timeout <= 0) { return Promise.reject(error); } config.timeout = timeout; } config.transformRequest = [(data) => data]; await onRetry(currentState.retryCount, error, config); if (config.signal?.aborted) { return Promise.resolve(axiosInstance(config)); } return new Promise((resolve) => { const abortListener = () => { clearTimeout(timeout); resolve(axiosInstance(config)); }; const timeout = setTimeout(() => { resolve(axiosInstance(config)); if (config.signal?.removeEventListener) { config.signal.removeEventListener('abort', abortListener); } }, delay); if (config.signal?.addEventListener) { config.signal.addEventListener('abort', abortListener, { once: true }); } }); } async function handleMaxRetryTimesExceeded(currentState, error) { if (currentState.retryCount >= currentState.retries) await currentState.onMaxRetryTimesExceeded(error, currentState.retryCount); } const axiosRetry = (axiosInstance, defaultOptions) => { const requestInterceptorId = axiosInstance.interceptors.request.use((config) => { setCurrentState(config, defaultOptions, true); if (config[namespace]?.validateResponse) { // by setting this, all HTTP responses will be go through the error interceptor first config.validateStatus = () => false; } return config; }); const responseInterceptorId = axiosInstance.interceptors.response.use(null, async (error) => { const { config } = error; // If we have no information to retry the request if (!config) { return Promise.reject(error); } const currentState = setCurrentState(config, defaultOptions); if (error.response && currentState.validateResponse?.(error.response)) { // no issue with response return error.response; } if (await shouldRetry(currentState, error)) { return handleRetry(axiosInstance, currentState, error, config); } await handleMaxRetryTimesExceeded(currentState, error); return Promise.reject(error); }); return { requestInterceptorId, responseInterceptorId }; }; // Compatibility with CommonJS axiosRetry.isNetworkError = isNetworkError; axiosRetry.isSafeRequestError = isSafeRequestError; axiosRetry.isIdempotentRequestError = isIdempotentRequestError; axiosRetry.isNetworkOrIdempotentRequestError = isNetworkOrIdempotentRequestError; axiosRetry.exponentialDelay = exponentialDelay; axiosRetry.linearDelay = linearDelay; axiosRetry.isRetryableError = isRetryableError; var isNode = typeof process !== 'undefined' && process.versions != null && process.versions.node != null; var AbortError = /** @class */ (function (_super) { __extends(AbortError, _super); function AbortError(message) { if (message === void 0) { message = 'Operation aborted'; } var _this = _super.call(this, message) || this; _this.name = 'AbortError'; return _this; } return AbortError; }(Error)); /** * Client for handling resumable file uploads * * Supports chunked uploads with progress tracking, resuming interrupted uploads, * and works in both browser and Node.js environments. */ var UploadxClient = /** @class */ (function () { /** * Creates a new UploadxClient instance * @param config - Configuration options for chunk size, retry behavior, and axios settings */ function UploadxClient(config) { if (config === void 0) { config = {}; } this.abortController = new AbortController(); this.chunkSize = config.chunkSize || 5 * 1024 * 1024; this.client = axios.create(__assign({ maxBodyLength: Number.POSITIVE_INFINITY, maxContentLength: Number.POSITIVE_INFINITY, timeout: 60000, validateStatus: function (status) { return (status >= 200 && status < 400) || status === 308; }, signal: this.abortController.signal }, config.requestConfig)); axiosRetry(this.client, __assign({ retries: 5, retryDelay: exponentialDelay, retryCondition: function (error) { var _a, _b; var isNetworkError = axiosRetry.isNetworkError(error) || error.code === 'ECONNRESET'; return (isNetworkError || axiosRetry.isRetryableError(error) || ((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 429 || (((_b = error.response) === null || _b === void 0 ? void 0 : _b.status) || 0) >= 500); } }, config.retryConfig)); } /** * Aborts all ongoing upload operations * Cancels any in-progress uploads initiated by this client instance */ UploadxClient.prototype.abort = function () { this.abortController.abort(); // Create a new controller for future operations this.abortController = new AbortController(); }; /** * Deletes an existing upload from the server */ UploadxClient.prototype.deleteUpload = function (url, signal) { return __awaiter(this, void 0, void 0, function () { var error_1; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); return [4 /*yield*/, this.client.delete(url, { signal: signal })]; case 1: _a.sent(); return [3 /*break*/, 3]; case 2: error_1 = _a.sent(); throw this.handleError(error_1, 'Failed to delete upload'); case 3: return [2 /*return*/]; } }); }); }; /** * * Updates the metadata of an existing upload */ UploadxClient.prototype.updateUpload = function (url, metadata, signal) { return __awaiter(this, void 0, void 0, function () { var error_2; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); return [4 /*yield*/, this.client.patch(url, metadata, { headers: { 'Content-Type': 'application/json', }, signal: signal, })]; case 1: _a.sent(); return [3 /*break*/, 3]; case 2: error_2 = _a.sent(); throw this.handleError(error_2, 'Failed to update metadata'); case 3: return [2 /*return*/]; } }); }); }; /** * Creates a new upload session on the server */ UploadxClient.prototype.createUpload = function (endpoint, metadata, signal) { return __awaiter(this, void 0, void 0, function () { var response, rangeHeader, error_3; var _a; return __generator(this, function (_b) { switch (_b.label) { case 0: _b.trys.push([0, 2, , 3]); return [4 /*yield*/, this.client.post(endpoint, metadata, { headers: { 'X-Upload-Content-Length': metadata.size, 'X-Upload-Content-Type': metadata.mimeType || 'application/octet-stream', 'Content-Type': 'application/json', }, signal: signal, })]; case 1: response = _b.sent(); if (!response.headers.location) { throw new Error('Missing Location header in response'); } rangeHeader = (_a = response === null || response === void 0 ? void 0 : response.headers) === null || _a === void 0 ? void 0 : _a.range; return [2 /*return*/, { url: new URL(response.headers.location, endpoint).toString(), uploadedBytes: this.parseRangeHeader(rangeHeader), }]; case 2: error_3 = _b.sent(); throw this.handleError(error_3, 'Session creation failed'); case 3: return [2 /*return*/]; } }); }); }; /** * Creates an upload session for a file in Node.js environment */ UploadxClient.prototype.createFileUpload = function (filePath, metadata, endpoint, signal) { return __awaiter(this, void 0, void 0, function () { var fsPromises, stats, fileSize, actualMetadata; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!isNode) { throw new Error('uploadAsFile is only available in Node.js environment'); } return [4 /*yield*/, import('node:fs/promises')]; case 1: fsPromises = _a.sent(); return [4 /*yield*/, fsPromises.stat(filePath)]; case 2: stats = _a.sent(); fileSize = stats.size; actualMetadata = __assign(__assign({}, metadata), { size: fileSize, lastModified: metadata.lastModified || stats.mtimeMs }); return [2 /*return*/, this.createUpload(endpoint, actualMetadata, signal)]; } }); }); }; /** * Uploads a file from disk (Node.js only) */ UploadxClient.prototype.fileUpload = function (endpoint, filePath, metadata, onProgress, signal) { return __awaiter(this, void 0, void 0, function () { var session, sessionUrl, start, totalSize, error_4; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 3, , 4]); return [4 /*yield*/, this.createFileUpload(filePath, metadata, endpoint, signal)]; case 1: session = _a.sent(); sessionUrl = session.url; start = session.uploadedBytes || 0; totalSize = metadata.size; return [4 /*yield*/, this.uploadFileInChunks(sessionUrl, filePath, start, totalSize, onProgress, signal)]; case 2: _a.sent(); return [3 /*break*/, 4]; case 3: error_4 = _a.sent(); // Don't throw if the operation was aborted if (error_4 instanceof AbortError) { return [2 /*return*/]; } throw this.handleError(error_4, "File upload failed for ".concat(filePath)); case 4: return [2 /*return*/]; } }); }); }; /** * Uploads data from various sources (Blob, File, Stream, Buffer) */ UploadxClient.prototype.upload = function (endpoint, data, metadata, onProgress, signal) { return __awaiter(this, void 0, void 0, function () { var _a, url, uploadedBytes, start, totalSize, error_5; return __generator(this, function (_b) { switch (_b.label) { case 0: _b.trys.push([0, 3, , 4]); return [4 /*yield*/, this.createUpload(endpoint, metadata, signal)]; case 1: _a = _b.sent(), url = _a.url, uploadedBytes = _a.uploadedBytes; start = uploadedBytes || 0; totalSize = metadata.size; return [4 /*yield*/, this.uploadDataInChunks(data, url, start, totalSize, onProgress, signal)]; case 2: _b.sent(); return [3 /*break*/, 4]; case 3: error_5 = _b.sent(); // Don't throw if the operation was aborted if (error_5 instanceof AbortError) { return [2 /*return*/]; } throw this.handleError(error_5, 'Upload failed'); case 4: return [2 /*return*/]; } }); }); }; /** * Resumes an upload from a previously created session */ UploadxClient.prototype.resumeUpload = function (url, data, metadata, onProgress, signal) { return __awaiter(this, void 0, void 0, function () { var response, start, totalSize, error_6; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 3, , 4]); return [4 /*yield*/, this.getUploadStatus(url, metadata, signal)]; case 1: response = _a.sent(); start = response.uploadedBytes; totalSize = metadata.size; return [4 /*yield*/, this.uploadDataInChunks(data, url, start, totalSize, onProgress, signal)]; case 2: _a.sent(); return [3 /*break*/, 4]; case 3: error_6 = _a.sent(); // Don't throw if the operation was aborted if (error_6 instanceof AbortError) { return [2 /*return*/]; } throw this.handleError(error_6, 'Upload failed'); case 4: return [2 /*return*/]; } }); }); }; /** * Resumes a file upload from disk (Node.js only) */ UploadxClient.prototype.resumeFileUpload = function (url, filePath, metadata, onProgress, signal) { return __awaiter(this, void 0, void 0, function () { var response, start, totalSize, error_7; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!isNode) { throw new Error('uploadAsFile is only available in Node.js environment'); } _a.label = 1; case 1: _a.trys.push([1, 4, , 5]); return [4 /*yield*/, this.getUploadStatus(url, metadata, signal)]; case 2: response = _a.sent(); start = response.uploadedBytes; totalSize = metadata.size; return [4 /*yield*/, this.uploadFileInChunks(url, filePath, start, totalSize, onProgress, signal)]; case 3: _a.sent(); return [3 /*break*/, 5]; case 4: error_7 = _a.sent(); // Don't throw if the operation was aborted if (error_7 instanceof AbortError) { return [2 /*return*/]; } throw this.handleError(error_7, "File upload failed for ".concat(filePath)); case 5: return [2 /*return*/]; } }); }); }; /** * Gets the current upload progress from the server */ UploadxClient.prototype.getUploadStatus = function (url, metadata, signal) { return __awaiter(this, void 0, void 0, function () { var headers, response, rangeHeader, uploadedBytes; var _a; return __generator(this, function (_b) { switch (_b.label) { case 0: headers = { 'Content-Type': 'application/octet-stream', 'Content-Range': "bytes */".concat((metadata === null || metadata === void 0 ? void 0 : metadata.size) || '*'), }; return [4 /*yield*/, this.client.put(url, null, { headers: headers, signal: signal, })]; case 1: response = _b.sent(); rangeHeader = (_a = response === null || response === void 0 ? void 0 : response.headers) === null || _a === void 0 ? void 0 : _a.range; uploadedBytes = this.parseRangeHeader(rangeHeader); return [2 /*return*/, { uploadedBytes: uploadedBytes }]; } }); }); }; UploadxClient.prototype.uploadChunk = function (url, data, start, end, totalSize, onProgress, signal) { return __awaiter(this, void 0, void 0, function () { var headers, response, rangeHeader, uploadedBytes; var _a; return __generator(this, function (_b) { switch (_b.label) { case 0: headers = { 'Content-Type': 'application/octet-stream', 'Content-Range': "bytes ".concat(start, "-").concat(end - 1, "/").concat(totalSize), }; return [4 /*yield*/, this.client.put(url, data, { headers: headers, signal: signal, onUploadProgress: onProgress ? function (progressEvent) { var chunkUploaded = progressEvent.loaded; var totalUploaded = start + chunkUploaded; var progress = totalUploaded / totalSize; onProgress(Math.min(progress, 1)); } : undefined, })]; case 1: response = _b.sent(); rangeHeader = (_a = response === null || response === void 0 ? void 0 : response.headers) === null || _a === void 0 ? void 0 : _a.range; uploadedBytes = this.parseRangeHeader(rangeHeader); return [2 /*return*/, { uploadedBytes: uploadedBytes }]; } }); }); }; UploadxClient.prototype.uploadDataInChunks = function (data, url, start, totalSize, onProgress, signal) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: if (!this.isBlob(data)) return [3 /*break*/, 2]; return [4 /*yield*/, this.uploadBlobInChunks(url, data, start, totalSize, onProgress, signal)]; case 1: _a.sent(); return [3 /*break*/, 6]; case 2: if (!this.isStream(data)) return [3 /*break*/, 4]; return [4 /*yield*/, this.uploadStreamInChunks(url, data, start, totalSize, onProgress, signal)]; case 3: _a.sent(); return [3 /*break*/, 6]; case 4: return [4 /*yield*/, this.uploadBufferInChunks(url, data, start, totalSize, onProgress, signal)]; case 5: _a.sent(); _a.label = 6; case 6: return [2 /*return*/]; } }); }); }; UploadxClient.prototype.uploadFileInChunks = function (url, filePath, start, totalSize, onProgress, signal) { return __awaiter(this, void 0, void 0, function () { var createReadStream, position, end, fileChunk, result, newPosition; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!isNode) { throw new Error('uploadAsFile is only available in Node.js environment'); } return [4 /*yield*/, import('node:fs')]; case 1: createReadStream = (_a.sent()).createReadStream; position = start; _a.label = 2; case 2: if (!(position < totalSize)) return [3 /*break*/, 4]; // Check if the operation was aborted if (signal === null || signal === void 0 ? void 0 : signal.aborted) { throw new AbortError(); } end = Math.min(position + this.chunkSize, totalSize); fileChunk = createReadStream(filePath, { start: position, end: end, }); return [4 /*yield*/, this.uploadChunk(url, fileChunk, position, end, totalSize, onProgress, signal)]; case 3: result = _a.sent(); position = end; if (result.uploadedBytes !== undefined) { newPosition = result.uploadedBytes; if (newPosition > position) { position = newPosition; } } return [3 /*break*/, 2]; case 4: return [2 /*return*/]; } }); }); }; UploadxClient.prototype.uploadBlobInChunks = function (url, blob, start, totalSize, onProgress, signal) { return __awaiter(this, void 0, void 0, function () { var position, end, chunk; return __generator(this, function (_a) { switch (_a.label) { case 0: position = start; _a.label = 1; case 1: if (!(position < totalSize)) return [3 /*break*/, 3]; // Check if the operation was aborted if (signal === null || signal === void 0 ? void 0 : signal.aborted) { throw new AbortError(); } end = Math.min(position + this.chunkSize, totalSize); chunk = blob.slice(position, end); return [4 /*yield*/, this.uploadChunk(url, chunk, position, end, totalSize, onProgress, signal)]; case 2: _a.sent(); position = end; return [3 /*break*/, 1]; case 3: return [2 /*return*/]; } }); }); }; UploadxClient.prototype.uploadStreamInChunks = function (url, stream, start, totalSize, onProgress, signal) { return __awaiter(this, void 0, void 0, function () { var position, chunks, chunksSize; var _this = this; return __generator(this, function (_a) { if (!isNode) { throw new Error('Stream uploads are only available in Node.js environment'); } position = start; chunks = []; chunksSize = 0; return [2 /*return*/, new Promise(function (resolve, reject) { // Set up abort handler var abortHandler = function () { reject(new AbortError()); }; if (signal) { if (signal.aborted) { abortHandler(); return; } signal.addEventListener('abort', abortHandler); } stream.on('data', function (chunk) { chunks.push(chunk); chunksSize += chunk.length; var _loop_1 = function () { var end = Math.min(position + _this.chunkSize, totalSize); var chunkBuffer = Buffer.concat(chunks); chunks = [chunkBuffer.slice(end - position)]; chunksSize = chunkBuffer.length - (end - position); stream.pause(); _this.uploadChunk(url, chunkBuffer.slice(0, end - position), position, end, totalSize, onProgress, signal) .then(function (range) { position = end; stream.resume(); }) .catch(function (error) { // stream.destroy(error); reject(error); }); }; while (chunksSize >= _this.chunkSize) { _loop_1(); } }); stream.on('end', function () { return __awaiter(_this, void 0, void 0, function () { var end, chunkBuffer, error_8; return __generator(this, function (_a) { switch (_a.label) { case 0: if (signal) { signal.removeEventListener('abort', abortHandler); } if (!(chunks.length > 0)) return [3 /*break*/, 4]; // Check if the operation was aborted if (signal === null || signal === void 0 ? void 0 : signal.aborted) { reject(new AbortError()); return [2 /*return*/]; } end = Math.min(position + chunksSize, totalSize); chunkBuffer = Buffer.concat(chunks); _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); return [4 /*yield*/, this.uploadChunk(url, chunkBuffer, position, end, totalSize, onProgress, signal)]; case 2: _a.sent(); position += chunksSize; return [3 /*break*/, 4]; case 3: error_8 = _a.sent(); reject(error_8); return [2 /*return*/]; case 4: resolve(); return [2 /*return*/]; } }); }); }); stream.on('error', function (error) { if (signal) { signal.removeEventListener('abort', abortHandler); } reject(error); }); })]; }); }); }; UploadxClient.prototype.uploadBufferInChunks = function (url, buffer, start, totalSize, onProgress, signal) { return __awaiter(this, void 0, void 0, function () { var view, position, end, chunk; return __generator(this, function (_a) { switch (_a.label) { case 0: view = buffer instanceof ArrayBuffer ? new Uint8Array(buffer) : buffer; position = start; _a.label = 1; case 1: if (!(position < totalSize)) return [3 /*break*/, 3]; // Check if the operation was aborted if (signal === null || signal === void 0 ? void 0 : signal.aborted) { throw new AbortError(); } end = Math.min(position + this.chunkSize, totalSize); chunk = view.subarray(position, end); return [4 /*yield*/, this.uploadChunk(url, chunk, position, end, totalSize, onProgress, signal)]; case 2: _a.sent(); position = end; return [3 /*break*/, 1]; case 3: return [2 /*return*/]; } }); }); }; UploadxClient.prototype.isBlob = function (data) { return typeof Blob !== 'undefined' && data instanceof Blob; }; UploadxClient.prototype.isStream = function (data) { return !!data && typeof data.pipe === 'function'; }; UploadxClient.prototype.parseRangeHeader = function (range) { if (!range) return 0; var matches = range.match(/bytes=\d+-(\d+)/); return matches ? Number.parseInt(matches[1], 10) + 1 : 0; }; UploadxClient.prototype.handleError = function (error, context) { var _a, _b, _c; if (error instanceof AbortError) { return error; } if (axios.isAxiosError(error)) { return new Error("".concat(context, ": ").concat(error.message, " ").concat(((_c = (_b = (_a = error.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.error) === null || _c === void 0 ? void 0 : _c.message) || error.cause, " ")); } if (error instanceof Error) { return new Error("".concat(context, ": ").concat(error.message)); } return new Error(context); }; return UploadxClient; }()); // biome-ignore lint/suspicious/noExplicitAny: <explanation> window.UploadxClient = UploadxClient; return UploadxClient; })); //# sourceMappingURL=index.js.map