124 lines
3.5 KiB
JavaScript
124 lines
3.5 KiB
JavaScript
|
|
import { getContext } from '../../../../../../extensions.js';
|
|||
|
|
|
|||
|
|
const LWB_RULES_V2_KEY = 'LWB_RULES_V2';
|
|||
|
|
|
|||
|
|
let rulesTable = {};
|
|||
|
|
|
|||
|
|
export function loadRulesFromMeta() {
|
|||
|
|
try {
|
|||
|
|
const meta = getContext()?.chatMetadata || {};
|
|||
|
|
rulesTable = meta[LWB_RULES_V2_KEY] || {};
|
|||
|
|
} catch {
|
|||
|
|
rulesTable = {};
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export function saveRulesToMeta() {
|
|||
|
|
try {
|
|||
|
|
const meta = getContext()?.chatMetadata || {};
|
|||
|
|
meta[LWB_RULES_V2_KEY] = { ...rulesTable };
|
|||
|
|
getContext()?.saveMetadataDebounced?.();
|
|||
|
|
} catch {}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export function getRuleNode(path) {
|
|||
|
|
return rulesTable[path] || null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export function setRule(path, rule) {
|
|||
|
|
rulesTable[path] = { ...(rulesTable[path] || {}), ...rule };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export function clearRule(path) {
|
|||
|
|
delete rulesTable[path];
|
|||
|
|
saveRulesToMeta();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export function clearAllRules() {
|
|||
|
|
rulesTable = {};
|
|||
|
|
saveRulesToMeta();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export function getParentPath(absPath) {
|
|||
|
|
const parts = String(absPath).split('.').filter(Boolean);
|
|||
|
|
if (parts.length <= 1) return '';
|
|||
|
|
return parts.slice(0, -1).join('.');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 验证操作
|
|||
|
|
* @param {string} op - set/inc/push/pop/del
|
|||
|
|
* @param {string} absPath - 规范化路径
|
|||
|
|
* @param {*} payload - set的值 / inc的delta / push的值 / pop的值
|
|||
|
|
* @param {*} currentValue - 当前值
|
|||
|
|
*/
|
|||
|
|
export function validate(op, absPath, payload, currentValue) {
|
|||
|
|
const node = getRuleNode(absPath);
|
|||
|
|
const parentPath = getParentPath(absPath);
|
|||
|
|
const parentNode = parentPath ? getRuleNode(parentPath) : null;
|
|||
|
|
const isNewKey = currentValue === undefined;
|
|||
|
|
|
|||
|
|
// 父层 $lock:不允许新增/删除 key
|
|||
|
|
if (parentNode?.lock) {
|
|||
|
|
if (isNewKey && (op === 'set' || op === 'push')) {
|
|||
|
|
return { allow: false, reason: 'parent-locked' };
|
|||
|
|
}
|
|||
|
|
if (op === 'del') {
|
|||
|
|
return { allow: false, reason: 'parent-locked' };
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 当前 $ro:不允许改值
|
|||
|
|
if (node?.ro && (op === 'set' || op === 'inc')) {
|
|||
|
|
return { allow: false, reason: 'ro' };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 当前 $lock:不允许改结构
|
|||
|
|
if (node?.lock && (op === 'push' || op === 'pop' || op === 'del')) {
|
|||
|
|
return { allow: false, reason: 'lock' };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// set 数值约束
|
|||
|
|
if (op === 'set') {
|
|||
|
|
const num = Number(payload);
|
|||
|
|
if (Number.isFinite(num) && (node?.min !== undefined || node?.max !== undefined)) {
|
|||
|
|
let v = num;
|
|||
|
|
if (node.min !== undefined) v = Math.max(v, node.min);
|
|||
|
|
if (node.max !== undefined) v = Math.min(v, node.max);
|
|||
|
|
return { allow: true, value: v };
|
|||
|
|
}
|
|||
|
|
// enum
|
|||
|
|
if (node?.enum?.length) {
|
|||
|
|
if (!node.enum.includes(String(payload ?? ''))) {
|
|||
|
|
return { allow: false, reason: 'enum' };
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return { allow: true, value: payload };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// inc:返回最终值
|
|||
|
|
if (op === 'inc') {
|
|||
|
|
const delta = Number(payload);
|
|||
|
|
if (!Number.isFinite(delta)) return { allow: false, reason: 'delta-nan' };
|
|||
|
|
|
|||
|
|
const cur = Number(currentValue) || 0;
|
|||
|
|
let d = delta;
|
|||
|
|
|
|||
|
|
// step 限制
|
|||
|
|
if (node?.step !== undefined && node.step >= 0) {
|
|||
|
|
if (d > node.step) d = node.step;
|
|||
|
|
if (d < -node.step) d = -node.step;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let next = cur + d;
|
|||
|
|
|
|||
|
|
// range 限制
|
|||
|
|
if (node?.min !== undefined) next = Math.max(next, node.min);
|
|||
|
|
if (node?.max !== undefined) next = Math.min(next, node.max);
|
|||
|
|
|
|||
|
|
return { allow: true, value: next };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return { allow: true, value: payload };
|
|||
|
|
}
|