/* * api.js * Copyright (C) 2024 veypi * * Distributed under terms of the MIT license. */ class API { prefix = '' fetch = window.fetch constructor(prefix, fetch) { if (fetch) { this.fetch = fetch } if (typeof prefix === 'string') { this.prefix = prefix } if (!this.prefix.endsWith('/')) { this.prefix += '/' } } wrapFetch(fetch) { this.fetch = fetch } wrapUrl(url) { if (url.startsWith('http')) { return url } else if (url.startsWith('/')) { return this.prefix + url.slice(1) } else { return this.prefix + url } } async Fetch(url, method, opts, body) { url = this.wrapUrl(url) if (!opts) { opts = {} } if (!opts.headers) { opts.headers = {} } opts.method = method opts.body = JSON.stringify(body) if (!opts.headers['Content-Type']) { opts.headers['Content-Type'] = 'application/json' } if (opts.query && Object.keys(opts.query).length && url.indexOf('?') === -1) { url += '?' + new URLSearchParams(opts.query).toString() } return this.fetch.bind(window)(url, opts).then((response) => { if (!response.ok) { throw response } let contentType = response.headers.get('content-type') if (contentType && contentType.includes('application/json')) { return response.json() } return response.text() }).then(data => { if (typeof data === 'string') { return data } if (data.code === 0) { return data.data } else if (data.code >= 0) { throw new Error(JSON.stringify(data)) } else { return data } }) } async Get(url, opts) { return this.Fetch(url, 'GET', opts) } async Post(url, data, opts) { return this.Fetch(url, 'POST', opts, data) } async Put(url, data, opts) { return this.Fetch(url, 'PUT', opts, data) } async Delete(url, opts) { return this.Fetch(url, 'DELETE', opts) } async Patch(url, data, opts) { return this.Fetch(url, 'PATCH', opts, data) } async SSE(url, opts, cb) { url = this.wrapUrl(url) let response = await this.fetch.bind(window)(url, opts) const reader = response.body.getReader(); const decoder = new TextDecoder('utf-8'); let count = 0 let partialLine = '' try { while (true) { const { done, value } = await reader.read(); if (done) break; // 结束时退出循环 // 将Uint8Array转换为字符串 const chunk = decoder.decode(value, { stream: true }); // 处理可能跨多个块的消息 const lines = (partialLine + chunk).split('\n'); partialLine = lines.pop(); // 最后一行可能是未完成的消息 for (const line of lines) { cb(line, count++) } } // 处理最后一个不完整的行 if (partialLine) { cb(partialLine, count++) } } catch (error) { console.error(error); } finally { reader.releaseLock(); cb('', -1) } } New(prefix) { return new API(prefix, this.fetch) } } export default new API()