+10
-4
hosting-service/src/lib/utils.ts
+10
-4
hosting-service/src/lib/utils.ts
······
+43
-12
hosting-service/src/server.ts
+43
-12
hosting-service/src/server.ts
···+console.warn(`File ${filePath} has gzip encoding in meta but content lacks gzip magic bytes`);·········+const hasGzipMagic = indexContent.length >= 2 && indexContent[0] === 0x1f && indexContent[1] === 0x8b;
+162
-8
public/editor/tabs/UploadTab.tsx
+162
-8
public/editor/tabs/UploadTab.tsx
······+const [failedFiles, setFailedFiles] = useState<Array<{ name: string; index: number; error: string; size: number }>>([])············+className="w-full p-3 bg-muted/50 hover:bg-muted transition-colors flex items-center justify-between text-sm font-medium"+Processing files ({fileProgressList.filter(f => f.status === 'uploaded' || f.status === 'reused').length}/{fileProgressList.length})
+3
src/lib/upload-jobs.ts
+3
src/lib/upload-jobs.ts
···phase: 'validating' | 'compressing' | 'uploading' | 'creating_manifest' | 'finalizing' | 'done';···
+9
-2
src/lib/wisp-utils.ts
+9
-2
src/lib/wisp-utils.ts
·········
+172
-32
src/routes/wisp.ts
+172
-32
src/routes/wisp.ts
···console.log(`Processing file ${i + 1}/${fileArray.length}:`, file.name, file.size, 'bytes');···-const compressionRatio = (compressedContent.length / originalContent.length * 100).toFixed(1);-console.log(`Compressing ${file.name}: ${originalContent.length} -> ${compressedContent.length} bytes (${compressionRatio}%), base64: ${base64Content.length} bytes`);-logger.info(`Compressing ${file.name}: ${originalContent.length} -> ${compressedContent.length} bytes (${compressionRatio}%), base64: ${base64Content.length} bytes`);+const compressionRatio = (compressedContent.length / originalContent.length * 100).toFixed(1);+console.log(`Compressing+base64 ${file.name}: ${originalContent.length} -> ${compressedContent.length} bytes (${compressionRatio}%), base64: ${finalContent.length} bytes`);+logger.info(`Compressing+base64 ${file.name}: ${originalContent.length} -> ${compressedContent.length} bytes (${compressionRatio}%), base64: ${finalContent.length} bytes`);+const compressionRatio = (compressedContent.length / originalContent.length * 100).toFixed(1);+console.log(`Compressing ${file.name}: ${originalContent.length} -> ${compressedContent.length} bytes (${compressionRatio}%)`);+logger.info(`Compressing ${file.name}: ${originalContent.length} -> ${compressedContent.length} bytes (${compressionRatio}%)`);+console.log(`Uploading ${file.name} directly: ${originalContent.length} bytes (no compression)`);···+console.log(`[File Upload] Starting upload attempt ${attempt + 1}/${maxRetries} for ${fileName} (${content.length} bytes, ${mimeType})`);-logger.info(`[File Upload] ๐ DPoP nonce conflict for ${fileName}, retrying in ${backoffMs}ms (attempt ${attempt + 1}/${maxRetries})`);+const isRateLimited = error?.status === 429 || error?.message?.toLowerCase().includes('rate');+const reason = isDPoPNonceError ? 'DPoP nonce conflict' : isTimeout ? 'timeout' : 'rate limit';+logger.info(`[File Upload] ๐ ${reason} for ${fileName}, retrying in ${backoffMs}ms (attempt ${attempt + 1}/${maxRetries})`);+logger.error(`[File Upload] โ Upload failed for ${fileName} (size: ${content.length} bytes, mimeType: ${mimeType}, attempt: ${attempt + 1}/${maxRetries})`, {·········-updateJobProgress(jobId, { filesReused: (getUploadJob(jobId)?.progress.filesReused || 0) + 1 });······-updateJobProgress(jobId, { filesUploaded: (getUploadJob(jobId)?.progress.filesUploaded || 0) + 1 });······+logger.error(`Upload failed for file: ${fileName} (${fileSize} bytes) at index ${index}`, errorDetails);+console.error(`Upload failed for file: ${fileName} (${fileSize} bytes) at index ${index}`, errorDetails);······+console.error(`[Concurrency] File ${currentIndex} (${file.name}) had unexpected error:`, error);+console.log(`[Concurrency] File ${currentIndex} (${file.name}) removed. Remaining ${executing.size}: [${remaining.join(', ')}]`);+console.log(`[Concurrency] Added file ${currentIndex} (${file.name}). Total ${executing.size}: [${current.join(', ')}]`);+console.log(`[Concurrency] Hit limit (${CONCURRENCY_LIMIT}), waiting for one to complete...`);+console.log(`[Concurrency] Waiting for ${executing.size} remaining uploads: [${remaining.join(', ')}]`);+return results.filter(r => r !== undefined && r !== null); // Filter out null (failed) and undefined entries···-logger.info(`[File Upload] ๐ Upload complete โ ${uploadedBlobs.length}/${validUploadedFiles.length} files (${currentUploaded} uploaded, ${currentReused} reused)`);+logger.info(`[File Upload] ๐ Upload complete โ ${successfulCount}/${validUploadedFiles.length} files succeeded (${currentUploaded} uploaded, ${currentReused} reused), ${failedCount} failed`);+console.warn(`[File Upload] โ ๏ธ ${failedCount} files failed to upload:`, failedFiles.map(f => f.name).join(', '));-logger.info(`[File Upload] ๐ Upload phase complete! Total: ${uploadedBlobs.length} files (${uploadedCount} uploaded, ${reusedCount} reused)`);+logger.info(`[File Upload] ๐ Upload phase complete! Total: ${successfulCount} files (${uploadedCount} uploaded, ${reusedCount} reused)`);···