@lcap/nasl
Version:
NetEase Application Specific Language
371 lines • 15.3 kB
JavaScript
;
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