/** * 把可移植 renderer 描述与受信 React 组件装配为 React Flow 类型表。 * 验证版本/结构及 rendererId 冲突,并通过弱缓存稳定 memo 包装身份;不实例化 editor 或元素模型。 */ import {memo} from 'react'; import type { NodeTypes, EdgeTypes } from '@xyflow/react'; import type { ComponentDefinition, Registry } from '../canvas-core/types.ts'; import { fault } from '../canvas-core/values.ts'; import { createRendererCatalog, type RendererDescriptor, type RendererCatalog } from './renderers.ts'; /** 节点/连线分别提供对应组件,可另提供渐进预览组件;模型解释协议仍由 RendererDescriptor 决定。 */ export type ReactRendererDefinition=RendererDescriptor&({readonly structure:'node';readonly render:NodeTypes[string];readonly preview?:NodeTypes[string]}|{readonly structure:'connection';readonly render:EdgeTypes[string];readonly preview?:EdgeTypes[string]}); /** 冻结的装配结果,普通与 preview 类型表分开交给各显示路径。 */ export type ReactRenderers={readonly catalog:RendererCatalog;readonly definitions:readonly ReactRendererDefinition[];readonly nodeTypes:NodeTypes;readonly edgeTypes:EdgeTypes;readonly previewNodeTypes:NodeTypes;readonly previewEdgeTypes:EdgeTypes}; /** 应用扩展可独立安装模型定义与显示实现;二者各自校验,不把 React 函数保存进元素数据。 */ export type CanvasRuntimeExtension={readonly components?:readonly ComponentDefinition[];readonly renderers?:readonly ReactRendererDefinition[]}; // 同一组件引用只创建一次 memo 包装,弱键不永久保留不再使用的插件组件。 const memoizedRenderers=new WeakMap(); /** $$typeof 为 react.memo 则原样返回;其它受支持组件按原引用弱缓存 React.memo 包装。 * 不调用 render、不解析 lazy 内容,也不缓存组件状态;包装身份稳定不保证所有 props 变化都免渲染。 */ function compiledRenderer(render:NodeTypes[string]|EdgeTypes[string]):NodeTypes[string]|EdgeTypes[string]{ if((render as {$$typeof?:symbol}).$$typeof===Symbol.for('react.memo'))return render; const key=render as object,previous=memoizedRenderers.get(key);if(previous)return previous; const compiled=memo(render as NodeTypes[string]);memoizedRenderers.set(key,compiled);return compiled; } /** 先验证纯描述,再验证 React 组件形状与可选模型 registry。 * 同一 structure 内 rendererId 可别名共用,但普通/预览组件引用必须一致;node/edge 类型表彼此独立。 * 标记对象只做结构识别,不证明组件安全、纯净或必能成功渲染;安装来源仍须受信。 * 每次调用新建目录/表/外壳,只有组件 memo 包装共享;冻结注册表不冻结函数闭包或 React 内部状态。 */ export function createReactRenderers(definitions:readonly ReactRendererDefinition[],registry?:Registry):ReactRenderers{ // 类型表无对象原型,保留名也已被描述目录拒绝,防止类型键继承/原型污染歧义。 const catalog=createRendererCatalog(definitions),nodes:NodeTypes=Object.create(null),edges:EdgeTypes=Object.create(null),previewNodes:NodeTypes=Object.create(null),previewEdges:EdgeTypes=Object.create(null),captured:ReactRendererDefinition[]=[]; for(let index=0;index