From 5b3bbdf02cc1a0af4705c8e2545c7e85436b4799 Mon Sep 17 00:00:00 2001 From: ztimson Date: Mon, 13 Jul 2026 22:16:58 -0400 Subject: [PATCH] Added node support for uploadWithProgress --- package.json | 2 +- src/files.ts | 46 ++++++++++++++++++++++++++++++++++------------ 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 03fba47..4453623 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ztimson/utils", - "version": "0.29.5", + "version": "0.29.6", "description": "Utility library", "author": "Zak Timson", "license": "MIT", diff --git a/src/files.ts b/src/files.ts index d4c3139..3321f59 100644 --- a/src/files.ts +++ b/src/files.ts @@ -83,6 +83,7 @@ export function timestampFilename(name?: string, date: Date | number | string = /** * Upload file to URL with progress callback using PromiseProgress + * Works in both browser (with progress) and Node.js (fallback without progress) * * @param {{url: string, files: File[], headers?: {[p: string]: string}, withCredentials?: boolean}} options * @return {PromiseProgress} Promise of request with `onProgress` callback @@ -93,19 +94,40 @@ export function uploadWithProgress(options: { headers?: {[key: string]: string}; withCredentials?: boolean; }): PromiseProgress { - return new PromiseProgress((res, rej, prog) => { - const xhr = new XMLHttpRequest(); - const formData = new FormData(); - options.files.forEach(f => formData.append('file', f)); + // Browser environment - use XMLHttpRequest for progress + if (typeof XMLHttpRequest !== 'undefined') { + return new PromiseProgress((res, rej, prog) => { + const xhr = new XMLHttpRequest(); + const formData = new FormData(); + options.files.forEach(f => formData.append('files', f)); - xhr.withCredentials = !!options.withCredentials; - xhr.upload.addEventListener('progress', (event) => event.lengthComputable ? prog(event.loaded / event.total) : null); - xhr.addEventListener('loadend', () => res(JSONAttemptParse(xhr.responseText))); - xhr.addEventListener('error', () => rej(JSONAttemptParse(xhr.responseText))); - xhr.addEventListener('timeout', () => rej({error: 'Request timed out'})); + xhr.withCredentials = !!options.withCredentials; + xhr.upload.addEventListener('progress', (event) => event.lengthComputable ? prog(event.loaded / event.total) : null); + xhr.addEventListener('loadend', () => res(JSONAttemptParse(xhr.responseText))); + xhr.addEventListener('error', () => rej(JSONAttemptParse(xhr.responseText))); + xhr.addEventListener('timeout', () => rej({error: 'Request timed out'})); - xhr.open('POST', options.url); - Object.entries(options.headers || {}).forEach(([key, value]) => xhr.setRequestHeader(key, value)); - xhr.send(formData); + xhr.open('POST', options.url); + Object.entries(options.headers || {}).forEach(([key, value]) => xhr.setRequestHeader(key, value)); + xhr.send(formData); + }); + } + + // Node.js environment - fallback to fetch without progress + return new PromiseProgress(async (res, rej) => { + try { + const formData = new FormData(); + options.files.forEach(f => formData.append('files', f)); + const response = await fetch(options.url, {method: 'POST', headers: options.headers || {}, body: formData}); + if(!response.ok) { + const error = await response.text(); + rej(JSONAttemptParse(error)); + } else { + const result = await response.text(); + res(JSONAttemptParse(result)); + } + } catch (error) { + rej(error); + } }); }