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 };
|
||
}
|