/** * ReactFlow适配层的保留标记缓存。为需要保留的节点/边复制一份带__endoleapRetain的投影记录,原对象不变。 * 标记供应用固定版本的编译扩展使用,不是模型属性,也不应进入history、存储或业务renderer数据。 */ /** Adapter-only flags consumed by the app's pinned React Flow compiler extension. * They never enter the document, history, persistence or renderer business data. */ /** 每个投影使用者创建一份缓存:WeakMap复用同一原对象的带标记副本,previous复用最近一次整个输出。 */ export function createRetainedProjection(){ // 弱引用键避免缓存永久占住已被替换的投影对象;只按对象身份缓存,不监测对象内部原地修改。 const tagged=new WeakMap(); let previous:{nodes:readonly object[];edges:readonly object[];ids:Set;result:{nodes:object[];edges:object[]}}|undefined; /** 输入数组和保留ID集合生成节点/边数组;ids为空时直接返回原数组并清除整批缓存。 */ return (nodes:N[],edges:E[],ids:ReadonlySet):{nodes:N[];edges:E[]}=>{ if(!ids.size){previous=undefined;return{nodes,edges};} // 数组引用及ID成员都相同才复用;快照复制ids,避免调用方之后原地修改Set影响比较基准。 if(previous?.nodes===nodes&&previous.edges===edges&&previous.ids.size===ids.size&&[...ids].every(id=>previous!.ids.has(id)))return previous.result as {nodes:N[];edges:E[]}; /** 未命中的记录保留原引用;命中时浅复制顶层加标记,嵌套数据仍与原投影共享。 */ const retain=(row:T):T=>{ if(!ids.has(row.id))return row; let value=tagged.get(row);if(!value){value={...row,__endoleapRetain:true};tagged.set(row,value);}return value as T; }; // 非空集合下会映射两张数组,即使没有命中也生成数组;下一次相同输入再复用该result。 const result={nodes:nodes.map(retain),edges:edges.map(retain)}; previous={nodes,edges,ids:new Set(ids),result};return result; }; }