UNPKG

@knxcloud/lowcode-data-source

Version:
284 lines (283 loc) 8.71 kB
"use strict"; Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); const vue = require("vue"); const lowcodeUtils = require("@knxcloud/lowcode-utils"); function isFormData(o) { return lowcodeUtils.toString(o) === "[object FormData]"; } function serializeParams(obj) { const result = []; const applyItem = (key, val) => { if (val === null || val === void 0 || val === "") { return; } if (typeof val === "object") { result.push(`${key}=${encodeURIComponent(JSON.stringify(val))}`); } else { result.push(`${key}=${encodeURIComponent(String(val))}`); } }; if (isFormData(obj)) { obj.forEach((val, key) => applyItem(key, val)); } else { Object.keys(obj).forEach((key) => applyItem(key, obj[key])); } return result.join("&"); } function buildUrl(dataAPI, params) { if (!params) return dataAPI; const paramStr = serializeParams(params); if (paramStr) { return dataAPI.indexOf("?") > 0 ? `${dataAPI}&${paramStr}` : `${dataAPI}?${paramStr}`; } return dataAPI; } function find(o, k) { for (const key in o) { if (key.toLowerCase() === k) { return [o[key], key]; } } return []; } function isValidResponseType(type) { return lowcodeUtils.isString(type) && ["arrayBuffer", "blob", "formData", "json", "text"].includes(type); } function createFormData(data) { const formData = new FormData(); for (const key in data) { const value = data[key]; if (value instanceof Blob) { formData.append(key, value); } else { formData.append(key, String(value)); } } return formData; } const bodyParseStrategies = { "application/json": (data) => JSON.stringify(data), "multipart/form-data": (data) => lowcodeUtils.isPlainObject(data) ? createFormData(data) : data, "application/x-www-form-urlencoded": (data) => serializeParams(data) }; function parseRequestBody(contentType, data) { const parser = Object.keys(bodyParseStrategies).find( (key) => contentType.includes(key) ); return parser ? bodyParseStrategies[parser](data) : data; } class RequestError extends Error { constructor(message, code, data) { super(message); this.code = code; this.data = data; } } class Response { constructor(code, data) { this.code = code; this.data = data; } } async function request(options) { const { uri, method, timeout, params = {}, headers = {}, isCors, responseType = "json", ...restOptions } = options; let url; const requestHeaders = { Accept: "application/json", ...headers }; const fetchOptions = { method, headers: requestHeaders, credentials: "include", ...restOptions }; isCors && (fetchOptions.mode = "cors"); if (method === "GET" || method === "DELETE" || method === "OPTIONS") { url = buildUrl(uri, params); } else { url = uri; const [contentType, key] = find(requestHeaders, "content-type"); fetchOptions.body = parseRequestBody(contentType != null ? contentType : "application/json", params); if (contentType === "multipart/form-data") { key && delete requestHeaders[key]; } } if (timeout) { const controller = new AbortController(); fetchOptions.signal = controller.signal; setTimeout(() => controller.abort(), timeout); } const res = await fetch(url, fetchOptions); const code = res.status; if (code >= 200 && code < 300) { if (code === 204) { if (method === "DELETE") { return new Response(code, null); } else { throw new RequestError(res.statusText, code); } } else { if (!isValidResponseType(responseType)) { throw new RequestError(`invalid response type: ${responseType}`, -1); } return new Response(code, await res[responseType]()); } } else if (code >= 400) { try { const data = await res.json(); throw new RequestError(res.statusText, code, data); } catch (e) { throw new RequestError(res.statusText, code); } } throw new RequestError(res.statusText, code); } function createDataSource(config, { state, setState }, requestHandlersMap) { const data = vue.shallowRef(); const error = vue.shallowRef(); const status = vue.ref("init"); const loading = vue.computed(() => status.value === "loading"); const isInit = vue.computed( () => config.isInit ? exec(config.isInit, state) : false ); const isSync = vue.computed( () => config.isSync ? exec(config.isSync, state) : false ); const { willFetch = same, shouldFetch = alwaysTrue, dataHandler = (res) => res && Reflect.get(res, "data"), errorHandler = alwaysThrow } = config; const load = async (inputParams, otherOptions = {}) => { try { const { type, options, id } = config; const request2 = getRequestHandler(config, requestHandlersMap); if (!request2) { throw new Error("unsupport fetch type: " + type); } if (!shouldFetch()) { throw new Error(`the ${id} request should not fetch, please check the condition`); } const { inputHeaders = {}, assignToScope = true, ...inputOptions } = otherOptions; status.value = "loading"; const { params, headers, ...restOptions } = exec(options, state); const parsedOptions = await willFetch({ ...restOptions, ...inputOptions, params: lowcodeUtils.isPlainObject(params) && lowcodeUtils.isPlainObject(inputParams) ? { ...params, ...inputParams } : inputParams != null ? inputParams : params, headers: { ...lowcodeUtils.isPlainObject(headers) ? headers : {}, ...lowcodeUtils.isPlainObject(inputHeaders) ? inputHeaders : {} } }); const res = await request2(parsedOptions, { state, setState }); const _data = data.value = dataHandler(res); if (!lowcodeUtils.isUndefined(_data) && assignToScope) { setState({ [id]: _data }); } status.value = "loading"; return _data; } catch (err) { status.value = "error"; error.value = err; errorHandler(err); } }; return vue.reactive({ data, error, loading, status, isInit, isSync, load }); } const same = (v) => v; const alwaysTrue = () => true; const alwaysThrow = (e) => { throw e; }; function exec(val, state) { if (lowcodeUtils.isFunction(val)) { return val.call(state, state); } else if (lowcodeUtils.isPlainObject(val)) { return Object.keys(val).reduce((res, next) => { Reflect.set(res, next, exec(val[next], state)); return res; }, {}); } return val; } function getRequestHandler(config, requestHandlersMap) { var _a, _b; const { type, requestHandler } = config; if (type) { if (type === "custom" && requestHandler) { return requestHandler; } else { return type === "fetch" ? (_a = requestHandlersMap[type]) != null ? _a : request : (_b = requestHandlersMap[type]) != null ? _b : null; } } return request; } function createDataSourceEngine(config, context, requestHandlersMap = {}) { const dataSource = {}; const dataSourceMap = {}; const { list, dataHandler } = config; for (const config2 of list) { const mergedConfig = { dataHandler, ...config2 }; const _dataSource = createDataSource(mergedConfig, context, requestHandlersMap); const func = (params, otherOptions) => { const mergedOptions = { assignToScope: false, ...otherOptions }; return _dataSource.load(params, mergedOptions); }; dataSource[config2.id] = func; dataSourceMap[config2.id] = _dataSource; } const reloadDataSource = (id, params, otherOptions) => { if (id) { const dataSource2 = dataSourceMap[id]; if (!dataSource2) { throw new Error("dataSource not found, id: " + id); } return dataSource2.load(params, otherOptions); } const syncItems = []; const asyncItems = []; Object.keys(dataSourceMap).map((id2) => dataSourceMap[id2]).filter((ds) => ds.isInit).forEach((ds) => { ds.isSync ? syncItems.push(ds) : asyncItems.push(ds); }); const promises = [ ...asyncItems.map((ds) => ds.load()), syncItems.reduce( (res, next) => res.then(() => next.load()), Promise.resolve(null) ) ]; return Promise.all(promises); }; const needInit = () => Object.keys(dataSourceMap).some((id) => dataSourceMap[id].isInit); return { dataSource, dataSourceMap, reloadDataSource, shouldInit: needInit }; } exports.createDataSourceEngine = createDataSourceEngine; exports.fetchRequest = request; //# sourceMappingURL=lowcode-data-source.js.map