src/fetchJson.mjs

/**
 * 以瀏覽器與 node(18+) 內建之 fetch 取得 JSON 回應, 取代 axios(2026-09-07 起本套件不再依賴 axios)
 *
 * 與 axios 之語意對齊:
 *   - 網路錯誤或非 2xx: axios 會 reject, fetch 只在網路錯誤 reject, 故此處對 !res.ok 亦 reject, 統一為 { kind: 'request', status, message }
 *   - 回應非 JSON: axios 回原字串(呼叫端判 state 失敗), 此處 res.json() 失敗 reject { kind: 'parse', status, message }
 *   - 成功: resolve 解析後之 JSON(對應 axios 之 res.data)
 * 注意: node 端 fetch(undici) 不讀 HTTP_PROXY / HTTPS_PROXY 環境變數(axios 會), 需走 proxy 之部署須自行以 opt.dispatcher 注入 undici Agent
 *
 * @param {String} url 輸入請求網址字串
 * @param {Object} [opt={}] 輸入 fetch 選項物件(method / headers / body / signal / dispatcher 等原樣傳給 fetch), 預設 {}
 * @returns {Promise} 回傳 Promise, resolve 為 JSON 物件, reject 為 { kind: 'request'|'parse', status, message }
 */
async function fetchJson(url, opt = {}) {

    //fetch
    let res = null
    try {
        res = await fetch(url, opt)
    }
    catch (err) {
        return Promise.reject({ kind: 'request', status: 0, message: String((err && err.message) || err) })
    }

    //check status
    if (!res.ok) {
        return Promise.reject({ kind: 'request', status: res.status, message: `HTTP ${res.status}` })
    }

    //json
    try {
        return await res.json()
    }
    catch (err) {
        return Promise.reject({ kind: 'parse', status: res.status, message: String((err && err.message) || err) })
    }

}


export default fetchJson