t-comm
Version:
专业、稳定、纯粹的工具库
365 lines (362 loc) • 11.8 kB
JavaScript
import _regeneratorRuntime from '@babel/runtime/regenerator';
import { _ as __awaiter } from '../tslib.es6-848120af.js';
import { ab2str } from './helper.mjs';
/**
* 解析 H5 环境下的 SSE 数据流
* @param {Object} params - 参数对象
* @param {Function} params.success - 成功回调函数
* @param {Function} params.fail - 失败回调函数
* @param {Function} params.complete - 完成回调函数
* @param {Response} params.response - 响应对象
* @returns {Promise} 返回一个 Promise,处理 SSE 数据流
* @example
* ```ts
* const response = await fetch('/api/sse', { method: 'POST' });
* await parseSSEChunkInH5({
* response,
* success: (chunk, line) => console.log(chunk),
* fail: ({ response }) => console.error(response.status),
* complete: () => console.log('finish'),
* });
* ```
*/
var parseSSEChunkInH5 = function parseSSEChunkInH5(_ref) {
var success = _ref.success,
fail = _ref.fail,
complete = _ref.complete,
response = _ref.response;
return __awaiter(void 0, void 0, void 0, /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
var reader, decoder, processText;
return _regeneratorRuntime.wrap(function (_context2) {
while (1) switch (_context2.prev = _context2.next) {
case 0:
processText = function _processText(_ref2) {
var done = _ref2.done,
value = _ref2.value;
return __awaiter(this, void 0, void 0, /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
var chunk, buffers, i, line, _response;
return _regeneratorRuntime.wrap(function (_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
if (!done) {
_context.next = 1;
break;
}
complete === null || complete === void 0 ? void 0 : complete(true);
return _context.abrupt("return", Promise.resolve());
case 1:
chunk = decoder.decode(value);
buffers = chunk.toString().split(/\r?\n/);
i = 0;
while (i < buffers.length) {
line = buffers[i];
_response = line.slice(5);
success === null || success === void 0 ? void 0 : success(_response, line);
buffers.splice(i, 1);
}
return _context.abrupt("return", reader.read().then(processText));
case 2:
case "end":
return _context.stop();
}
}, _callee);
}));
};
if (response === null || response === void 0 ? void 0 : response.ok) {
_context2.next = 1;
break;
}
// 失败中不调用完成,如果需要,业务自行在 fail 中调用
// 防止业务在 fail 中调用 complete 时,complete 被调用两次
fail === null || fail === void 0 ? void 0 : fail({
response: response
});
// 抛出错误以便链式调用中的下一个.catch()处理
return _context2.abrupt("return", Promise.reject(new Error('Request failed')));
case 1:
reader = response.body.getReader();
decoder = new TextDecoder();
if (reader) {
_context2.next = 2;
break;
}
return _context2.abrupt("return", Promise.reject(new Error('[SSE] 获取reader失败')));
case 2:
return _context2.abrupt("return", reader.read().then(processText));
case 3:
case "end":
return _context2.stop();
}
}, _callee2);
}));
};
/**
* 在 H5 环境下发起 SSE 请求的核心函数
* @param {Object} params - 参数对象
* @param {string} params.url - 请求 URL
* @param {any} params.data - 请求数据
* @param {Function} params.success - 成功回调函数
* @param {Function} params.fail - 失败回调函数
* @param {Function} params.complete - 完成回调函数
* @returns {Promise} 返回一个 Promise,包含响应对象
* @example
* ```ts
* fetchSSECoreInH5({
* url: 'https://example.com/sse',
* data: { input: 'hello' },
* success: (chunk) => console.log(chunk),
* fail: ({ response }) => console.error('failed'),
* complete: () => console.log('done'),
* });
* ```
*/
function fetchSSECoreInH5(_ref3) {
var url = _ref3.url,
data = _ref3.data,
success = _ref3.success,
fail = _ref3.fail,
complete = _ref3.complete;
return new Promise(function (resolve, reject) {
var controller = new AbortController();
fetch(url, {
method: 'POST',
headers: {
accept: 'application/json, text/plain, */*',
'content-type': 'application/json;charset=UTF-8'
},
body: JSON.stringify(data),
credentials: 'include',
// mode: 'cors',
signal: controller.signal
}).then(function (response) {
resolve(response);
parseSSEChunkInH5({
response: response,
success: success,
fail: fail,
complete: complete
});
})["catch"](function (err) {
reject(err);
});
});
}
/**
* 获取登录 Cookie
* @param {Function} isTestEnv - 是否为测试环境的函数
* @returns {string} 返回拼接后的 Cookie 字符串
*/
function getLoginCookie(isTestEnv) {
var headerCookie = '';
var testEnv = isTestEnv === null || isTestEnv === void 0 ? void 0 : isTestEnv();
var cookieSuffix = testEnv ? '@test' : '@prod';
if (uni.getStorageSync("request-mp-set-cookie".concat(cookieSuffix))) {
headerCookie = uni.getStorageSync("request-mp-set-cookie".concat(cookieSuffix));
}
if (uni.getStorageSync("user-info-sessionid".concat(cookieSuffix))) {
// 如果是测试环境,需要设置到tiplogin_sessionid_test
var sessionKey = 'tiplogin_sessionid';
if (isTestEnv === null || isTestEnv === void 0 ? void 0 : isTestEnv()) {
sessionKey = 'tiplogin_sessionid_test';
}
headerCookie += ";".concat(sessionKey, "=").concat(uni.getStorageSync("user-info-sessionid".concat(cookieSuffix)), ";");
}
return headerCookie;
}
/**
* 解析 MP 环境下的 SSE 数据块
* @param {Object} params - 参数对象
* @param {string} params.chunk - 数据块
* @param {Fail} [params.fail] - 失败回调函数
* @param {Success} [params.success] - 成功回调函数
* @example
* ```ts
* parseSSEChunkInMP({
* chunk: 'data: hello\ndata: world\n',
* success: (data, line) => console.log(data),
* fail: () => console.error('parse error'),
* });
* ```
*/
var parseSSEChunkInMP = function parseSSEChunkInMP(_ref4) {
var chunk = _ref4.chunk,
fail = _ref4.fail,
success = _ref4.success;
var buffers = chunk.toString().split(/\r?\n/);
var i = 0;
try {
while (i < buffers.length) {
var line = buffers[i];
var response = line.slice(5);
success === null || success === void 0 ? void 0 : success(response, line);
buffers.splice(i, 1);
}
} catch (err) {
fail === null || fail === void 0 ? void 0 : fail(false);
}
};
/**
* 在 MP 环境下发起 SSE 请求的核心函数
* @param {RequestParams} params - 请求参数
* @returns {Promise} 返回一个 Promise,包含请求任务对象
* @example
* ```ts
* fetchSSECoreInMP({
* url: 'https://example.com/sse',
* data: { input: 'hello' },
* success: (data) => console.log(data),
* fail: (err) => console.error(err),
* complete: () => console.log('done'),
* isTestEnv: () => false,
* });
* ```
*/
function fetchSSECoreInMP(_ref5) {
var url = _ref5.url,
data = _ref5.data,
success = _ref5.success,
_fail = _ref5.fail,
complete = _ref5.complete,
isTestEnv = _ref5.isTestEnv;
return new Promise(function (resolve) {
// 开启分块接收,模拟 SSE
var requestTask = wx.request({
url: url,
method: 'POST',
responseType: 'arraybuffer',
enableChunked: true,
header: {
Accept: 'text/event-stream',
'content-type': 'application/json;charset=UTF-8',
cookie: getLoginCookie(isTestEnv),
'Cache-Control': 'no-cache'
},
data: data,
success: function success(res) {
console.log('[SSE] success: ', res);
complete === null || complete === void 0 ? void 0 : complete(true, res);
},
fail: function fail(err) {
console.log('[SSE] fail: ', err);
_fail === null || _fail === void 0 ? void 0 : _fail(err);
}
});
// 监听数据到达事件
requestTask.onChunkReceived(function (res) {
// res.data 是 ArrayBuffer,需要转换为字符串
var chunk = ab2str(res.data);
parseSSEChunkInMP({
chunk: chunk,
fail: _fail,
success: success
});
});
resolve(requestTask);
});
}
/**
* 发起 SSE 请求的核心函数,根据环境自动选择 H5 或 MP 实现
* @param {RequestParams} params - 请求参数
* @param {string} params.url - 请求地址
* @param {Object} params.data - 请求数据
* @param {Function} params.success - 成功回调
* @param {Function} params.fail - 失败回调
* @param {Function} params.complete - 完成回调
* @returns {Promise} 返回一个 Promise,包含请求任务或响应对象
* @example
* ```ts
* import { safeJsonParse } from 't-comm/es/json';
*
* import { fetchSSECore, type RequestParams } from 't-comm/es/sse';
*
* // 检查是否结束,业务自定义
* function checkFinish(str) {
* const data: any = safeJsonParse(str);
* return data?.status === 3;
* }
*
* // 业务二次封装,把自己的请求数据、自定义的处理数据的逻辑放进去
* export function sendChatMessage({
* input,
* sessionId,
* extraInfo,
* expectedOp,
*
* success,
* fail,
* complete,
* }: {
* input: string;
* sessionId?: string;
* extraInfo?: string;
* expectedOp?: Record<string, any>;
*
* success?: (data: any) => void;
* fail?: RequestParams['fail'];
* complete?: RequestParams['complete'];
* }) {
* const origin = isTestEnv() ? 'https://xx.com' : 'https://xx.com';
* const url = `${origin}/xx.xx.xx.xx/xx?g_app_tk=${cookie.get('tip_token')}&tstamp=${Date.now()}`;
* const reqData = {
* input,
* session_id: sessionId,
* extra_info: extraInfo,
* expected_op: expectedOp,
* };
*
* const parsedSuccess = (str, fullStr) => {
* const data: any = safeJsonParse(str);
* const fullData: any = safeJsonParse(fullStr);
*
* if (checkFinish(str)) {
* complete?.(str);
* return;
* }
*
* // 检查是否失败,业务自定义
* if (fullData?.err_msg) {
* fail?.(data);
* return;
* }
*
* success?.(data);
* };
*
* return fetchSSECore({
* url,
* data: reqData,
* success: parsedSuccess,
* fail,
* complete,
* isTestEnv,
* });
* }
*
* ```
*/
function fetchSSECore(_ref6) {
var url = _ref6.url,
data = _ref6.data,
success = _ref6.success,
fail = _ref6.fail,
complete = _ref6.complete,
isTestEnv = _ref6.isTestEnv;
if (typeof wx !== 'undefined' && typeof wx.request === 'function' && typeof fetch === 'undefined' && typeof AbortController === 'undefined') {
return fetchSSECoreInMP({
url: url,
data: data,
success: success,
fail: fail,
complete: complete,
isTestEnv: isTestEnv
});
}
return fetchSSECoreInH5({
url: url,
data: data,
success: success,
fail: fail,
complete: complete
});
}
export { fetchSSECore, fetchSSECoreInH5, fetchSSECoreInMP, parseSSEChunkInH5, parseSSEChunkInMP };