1(function registerApiClient(global) { 2 function createApiClient({ getToken }) { 3 async function api(path, options = {}) { 4 const headers = { 'Content-Type': 'application/json', ...(options.headers || {}) }; 5 const token = getToken(); 6 if (token) headers.Authorization = `Bearer ${token}`; 7 8 try { 9 const res = await fetch(path, { ...options, headers }); 10 if (!res.ok) { 11 // A 401 on an authenticated request means the token is gone or 12 // expired: drop straight to the login screen with no message, and 13 // never settle this promise so the caller's own error toast/alert 14 // cannot fire for it (standing rule 2026-09-02). 15 if (res.status === 401 && token && typeof global.handleSessionExpired === 'function') { 16 global.handleSessionExpired(); 17 return new Promise(() => {}); 18 } 19 let errorMessage = 'Request failed'; 20 const errorStatus = res.status; 21 try { 22 const err = await res.json(); 23 errorMessage = err.details 24 ? `${err.error || errorMessage}: ${err.details}` 25 : (err.error || errorMessage); 26 } catch (jsonErr) { 27 // Response is not JSON or has no body. 28 } 29 const error = new Error(errorMessage); 30 error.status = errorStatus; 31 throw error; 32 } 33 return res.json(); 34 } catch (networkErr) { 35 if (networkErr.status) throw networkErr; 36 console.error(`API Error [${path}]:`, networkErr); 37 const error = new Error('Network error. Please check your connection.'); 38 error.isNetworkError = true; 39 throw error; 40 } 41 } 42 43 async function apiUpload(path, formData) { 44 const headers = {}; 45 const token = getToken(); 46 if (token) headers.Authorization = `Bearer ${token}`; 47 48 try { 49 const res = await fetch(path, { 50 method: 'POST', 51 headers, 52 body: formData 53 }); 54 if (!res.ok) { 55 if (res.status === 401 && token && typeof global.handleSessionExpired === 'function') { 56 global.handleSessionExpired(); 57 return new Promise(() => {}); 58 } 59 let errorMessage = 'Upload failed'; 60 try { 61 const err = await res.json(); 62 errorMessage = err.details 63 ? `${err.error || errorMessage}: ${err.details}` 64 : (err.error || errorMessage); 65 } catch (jsonErr) {} 66 throw new Error(errorMessage); 67 } 68 return res.json(); 69 } catch (networkErr) { 70 if (networkErr.message && networkErr.message !== 'Failed to fetch') throw networkErr; 71 console.error(`Upload Error [${path}]:`, networkErr); 72 throw new Error('Network error during upload. Please try again.'); 73 } 74 } 75 76 return { api, apiUpload }; 77 } 78 79 global.AtixApiClient = { 80 createApiClient 81 }; 82})(window);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.