UNPKG

@lcap/nasl

Version:

NetEase Application Specific Language

371 lines 15.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.updateNaslCache = exports.getNaslCacheWithoutCheck = exports.canUseNaslCache = exports.update = void 0; const split_1 = require("./split"); const lodash_1 = require("lodash"); const nasl_utils_1 = require("@lcap/nasl-utils"); const service_1 = require("@lcap/nasl-concepts/service"); /** 禁用模式 */ const disabled = nasl_utils_1.isNode || nasl_utils_1.isAutoTestEnv; const globalChangedKey = 'appGlobalChanged'; function getNodes(node, onlyCurrent) { if (!node) { return []; } let newNode = node; if (newNode?.__v_raw) { newNode = newNode.__v_raw; } let newNodeJSON; if (onlyCurrent) { // 如果只需要自己的, 内部所有的文件级别节点 都不去遍历了, 其余的节点还是要的 function check(node) { if (node?.concept && (0, service_1.checkNodeIsFileModule)(node)) { return false; } return true; } newNodeJSON = newNode._toFileJSON(check); } else { newNodeJSON = newNode.toJSON(); } const fileNodes = (0, split_1.splitNaslObj)(newNodeJSON); const nodePath = node.nodePath; const nodeName = node.name; const res = fileNodes.map((item, index) => { const newItem = { ...item }; newItem.nodePath = item.nodePath.replace(nodeName, nodePath); newItem.parentNodePath = item.parentNodePath ? item.parentNodePath.replace(nodeName, nodePath) : undefined; if (!newItem?.parentKey || !newItem?.parentNodePath) { newItem.parentKey = node.parentKey; newItem.parentNodePath = node.parentNode?.nodePath; } return newItem; }); return res; } function removeLastSegment(path) { // 使用正则表达式匹配最后一个 .segment const newPath = path.replace(/\.[^.]+$/, ''); return newPath; } function getParentKeyAndParentPath(nodePath) { const lastIndex = nodePath.lastIndexOf('.'); // 获取parentNodePath const parentNodePath = nodePath.substring(0, lastIndex); // 获取parentKey const namePath = nodePath.substring(lastIndex + 1); const parentKeyMatch = namePath.match(/([^\.\[\]]+)\[/); const parentKey = parentKeyMatch ? parentKeyMatch[1] : null; return { parentKey, parentNodePath, }; } async function update({ res, err, instructList, operationList, app }) { const startTime = Date.now(); // 有错误就不更新缓存 if (err || !window?.$IndexedDBHelper) { return; } const { globalChangedTime, preGlobalChangedTime } = res; // 需要重新 tojson 的修改 const updateNodeSet = new Set(); // 需要删除的节点 const deleteNodeSet = new Set(); // 需要重新生成,并且子节点也需要重新生成 const updateChildNodeSet = new Set(); // 直接查库, 更新时间就好 const getCacheUpdateTimePaths = new Set(); // 直接查库, 删除就好 const getDeletePaths = []; // 要更新的节点 const changeRes = [ { nodePath: globalChangedKey, globalChangedTime, forceUpdate: false, } ]; instructList.forEach(instructItem => { instructItem.actions.forEach(actionItem => { const { path, action, object } = actionItem; let newPath = path; // 如果是新增,因为这里传入的 是 object 不是 nasl 节点,而且 nodepath 是修改前的,所以不准,需要重新生成 if (action === 'create' && object?.name) { const parts = path.split('.'); const lastPart = parts.pop(); const match = lastPart.match(/\[(\d+)\]/); if (match) { parts.push(lastPart.replace(/\[\d+\]/, `[name=${object?.name}]`)); } else { parts.push(lastPart); } newPath = parts.join('.'); } // delete的时候 有 name 都用的 name if (action === 'delete') { // 删了东西之后,节点肯定是不在了,所以现查一下现在数据库有没有这个 path 的,没有就往上找一层,更新上一层 getDeletePaths.push(path); } else if (action === 'create') { // 新增的时候都是有节点的, 所以直接查节点,和更新缓存就好了 let node = app.findNodeByPath(newPath); // 个别没有的还是查上一级, 通过上级找到他 if (!node?.concept) { const removeLastNewPath = removeLastSegment(newPath); node = app.findNodeByPath(removeLastNewPath); } if (node?.concept) { // 增加有嵌套式的, 需要吧内部的节点都捞出来 const fileNode = (0, service_1.getNodeFileModule)(node); // 只有新增的节点和nodePath是一样的,顶级节点才去看下一级有什么内容一起加上,否则只更新自己 if (node.concept === fileNode.concept && node.nodePath === fileNode.nodePath) { updateChildNodeSet.add(fileNode); } else { updateNodeSet.add(fileNode); } } else { console.error(path, 3333); } } else { if (Object.keys(object).length === 1 && object.changedTime) { getCacheUpdateTimePaths.add(path); return; } const node = app.findNodeByPath(path); if (node?.concept) { if (node.concept === 'App') { updateNodeSet.add(node); } else { const fileNode = (0, service_1.getNodeFileModule)(node); updateNodeSet.add(fileNode); } } else { if (object.name) { function updateLastLogicName(path, newName) { // 使用正则表达式匹配最后一个 [name=xxx] const regex = /\[name=[^\]]+\](?!.*\[name=[^\]]+\])/; // 使用新名字替换最后一个 [name=xxx] return path.replace(regex, `[name=${newName}]`); } const updatedPath = updateLastLogicName(path, object.name); const updatedNode = app.findNodeByPath(updatedPath); // 改名下面的子集也需要一起改名 updateChildNodeSet.add(updatedNode); deleteNodeSet.add(path); } } } }); }); const getDeleteNodes = await window?.$IndexedDBHelper.readDataBatch(getDeletePaths); getDeleteNodes.forEach((nodeItem, index) => { const path = getDeletePaths[index]; /** * 删除节点的大概情况: * 删除 logic1 这种节点本身,如果查到数据库有就删掉自身就好了, * 删除 view1 这种节点,本身,以及子节点都要删除,(这种可能要把子节点,都找到删除,比如删除一个端,先不处理,当只删除自身, 重新进入时,更新) * 删除 logic1 下的 if语句等, 需要冒泡到 逻辑本身上 * 删除 最后一个事件逻辑,可能还会删除 事件,然后冒泡的页面, 所以可能会找不到 node。 */ if (nodeItem.value) { deleteNodeSet.add(path); } else { const newPath = removeLastSegment(path); const node = app.findNodeByPath(newPath); if (node) { const fileNode = (0, service_1.getNodeFileModule)(node); updateNodeSet.add(fileNode); } } }); const getCacheUpdateTimeNodes = await window?.$IndexedDBHelper.readDataBatch([...getCacheUpdateTimePaths]); getCacheUpdateTimeNodes.forEach((nodeItem, index) => { if (nodeItem.value) { const item = nodeItem.value; changeRes.push(item); } else { const keys = getCacheUpdateTimePaths.keys(); const path = [...keys][index]; const node = app.findNodeByPath(path); if (node) { const fileNode = (0, service_1.getNodeFileModule)(node); updateNodeSet.add(fileNode); } } }); console.log('nasl storage data change: updateNodeSet, updateChildNodeSet, deleteNodeSet, changeRes'); console.log(updateNodeSet, updateChildNodeSet, deleteNodeSet, changeRes); if (updateNodeSet.size || updateChildNodeSet.size || changeRes.length) { const changeNodes = []; // 需要改自己和子集的 updateChildNodeSet.forEach((node) => { changeNodes.push(...getNodes(node)); }); // 只需要改自己的 updateNodeSet.forEach((node) => { changeNodes.push(...getNodes(node, true)); }); const processedNodes = []; const nodePathMap = new Map(); for (const node of [...changeNodes, ...changeRes]) { // 检查是否已存在 if (!nodePathMap.get(node.nodePath)) { // 因为tojson 之后的数据,可能被 vue 劫持,所以克隆 nasl 属性 if (changeNodes.includes(node)) { node.nasl = (0, lodash_1.cloneDeep)(node.nasl); } // 更新缓存时间 node._changedTime = globalChangedTime; if (node?.nasl?._changedTime) { node.nasl._changedTime = globalChangedTime; } nodePathMap.set(node.nodePath, true); processedNodes.push(node); } } nasl_utils_1.isDebugMode && console.dir(processedNodes); window?.$IndexedDBHelper.storeDataBatch(processedNodes); } if (deleteNodeSet.size) { window?.$IndexedDBHelper.storeDataRemoveBatch([...deleteNodeSet]); } // 需要看看是不是有别的 tab 或者内容修改, // 把本地存储的上次修改的时间拿出来, 用本次修改的返回的上次修改的时间,比较,如果不一样就要去 getquery // const oldChangeObj = await (window as any)?.$IndexedDBHelper.readData(globalChangedKey); // const newChangeObj = { // nodePath: globalChangedKey, // globalChangedTime, // forceUpdate: oldChangeObj?.forceUpdate || false, // } // if (oldChangeObj?.globalChangedTime !== preGlobalChangedTime) { // newChangeObj.forceUpdate = true // } // console.log(newChangeObj, 3333, 'newChangeObj') // (window as any)?.$IndexedDBHelper.storeData(newChangeObj); const endTime = Date.now(); if (endTime - startTime > 50) { console.log(`更新缓存耗时: ${endTime - startTime}ms`); } } exports.update = update; async function canUseNaslCache(globalChangedTime) { if (disabled) { return; } try { const data = await window?.$IndexedDBHelper.readData(globalChangedKey); // 缓存有缓存时间, 就是可以用缓存 return data?.globalChangedTime; } catch (error) { // 读取数据出错时,返回 false return false; } } exports.canUseNaslCache = canUseNaslCache; const getAppTreePaths = (obj, parentPath = '') => { let paths = []; for (let key in obj) { if (Array.isArray(obj[key])) { obj[key].forEach((item) => { let newPath = parentPath ? `${parentPath}.${key}[name=${item.name}]` : `${key}[name=${item.name}]`; paths.push(newPath); paths = paths.concat(getAppTreePaths(item, newPath)); }); } else if (typeof obj[key] === 'object') { let newPath = parentPath ? `${parentPath}.${key}` : `${key}`; paths = paths.concat(getAppTreePaths(obj[key], newPath)); } } return paths; }; /** * 取出缓存 * * @description 只是取出缓存,并不校验缓存的有效性 */ async function getNaslCacheWithoutCheck(allData, incrNasls, appTree) { const updateData = []; const deleteData = []; incrNasls.forEach((item) => { const findIndex = allData.findIndex(cacheItem => item.path === cacheItem.nodePath); if (item.type === 'delete') { allData.splice(findIndex, 1); deleteData.push(item.path); } else { const newItem = { nasl: item.nasl, nodePath: item.path, ...getParentKeyAndParentPath(item.path), _changedTime: item.nasl._changedTime, }; updateData.push(newItem); if (findIndex !== -1) { allData.splice(findIndex, 1, newItem); } else { allData.unshift(newItem); } } }); // 更新缓存 // 需要 await ,存储后,在进行下面的排序 和 合并 nasl ,合并 nasl 会改变对象中的值 await window?.$IndexedDBHelper.storeDataBatch(updateData); window?.$IndexedDBHelper.storeDataRemoveBatch(deleteData); // 进行排序 if (appTree) { // 因为 indexdb 拿出来的数据 都是没有顺序的,所以这里要按照 服务端返回的 nasl 的顺序来排列 // 解析 appTree 对象,获取路径的顺序 // 获取 appTree 的顺序 let paths = getAppTreePaths(appTree, 'app'); // 根据路径的顺序对 aaa 进行排序 allData.sort((a, b) => { let indexA = paths.indexOf(a.nodePath); let indexB = paths.indexOf(b.nodePath); return indexA - indexB; }); } // 合并 nasl const batchQueryRes = (0, split_1.mergeNaslObj)(allData); return batchQueryRes; } exports.getNaslCacheWithoutCheck = getNaslCacheWithoutCheck; async function updateNaslCache(data, globalChangedTime) { // 进行前端缓存 if (!window?.$IndexedDBHelper) { return; } console.time('拆分nasl'); const newFiles = (0, split_1.splitNaslObj)(data); console.timeEnd('拆分nasl'); // 请求过之后,全局更新一把 const newChangeObj = { nodePath: globalChangedKey, globalChangedTime, forceUpdate: false, }; newFiles.unshift(newChangeObj); console.time('存储数据库'); // 先清除所有数据库的内容 await window?.$IndexedDBHelper.clear(); // 更新存储数据库 await window?.$IndexedDBHelper.storeDataBatch(newFiles); console.timeEnd('存储数据库'); } exports.updateNaslCache = updateNaslCache; //# sourceMappingURL=index.js.map