Skip to content

插件运行时契约规范 ​

定位 ​

NEXA 产品工程只集成组件渲染、事件、状态、数据、命令、服务发现和生命周期等基础能力。跨组件联动、数据处理和产品业务流程由插件实现。组件不得直接引用其他组件,插件不得直接访问 Vue 实例、Pinia Store、Cesium Viewer 或产品内部路径。

插件数据分为两类:

  • PluginManifest:插件注册表保存的版本化声明,描述制品、依赖、权限、阶段和失败策略;
  • ProductPluginPlan:某个产品发布使用的插件装配快照,锁定精确版本、不可变入口、完整性摘要和实例配置。

数据库不得保存函数、表达式或已实例化对象。插件实现以不可变 ESM 制品发布,数据库只保存制品引用及可验证 JSON。

启动与渲染顺序 ​

Runtime 必须按以下流水线执行:

  1. 获取并校验产品组件装配数据;
  2. 获取并校验插件计划及每个插件 Manifest;
  3. 校验 Host Contract、精确版本、SRI、配置 Schema、依赖、权限和组件能力约束;
  4. 构建插件依赖有向图,发现缺失依赖、循环依赖或跨阶段逆序时立即失败;
  5. 按固定阶段执行插件并推进组件渲染;
  6. 产品卸载时按激活顺序的逆序释放插件。

固定阶段顺序:

text
bootstrap
  → before-components
  → 加载组件制品
  → components-ready
  → 挂载布局与 Renderer
  → after-render
  → interactive

同一阶段内采用稳定拓扑排序:

  1. requires.plugins、after 和 before 形成有向边;
  2. 当前可执行节点按 priority 从小到大选择;
  3. 优先级相同时按插件实例 ID 字典序选择;
  4. 依赖缺失、环、重复实例 ID和指向自身均为装配错误;
  5. before/after 只能引用同阶段插件,跨阶段关系由阶段顺序表达。

上下文与权限 ​

Runtime 为每个插件创建独立 PluginContext,并依据 Manifest 对端口做最小权限裁剪:

  • events:发布和订阅白名单事件;
  • state:读取、写入和订阅命名空间;
  • services:发现并调用 Capability;
  • components:梳理组件快照、发送受控命令,并按组件实例 ID 绑定运行时 Props 与事件;
  • data:通过命名数据源查询、变更或订阅数据;
  • commands:执行宿主已注册命令;
  • lifecycle:登记清理函数;
  • logger:记录带插件身份的结构化日志。

未声明的上下文能力必须拒绝访问。端口返回 unknown,插件必须自行校验外部数据,禁止用泛型断言伪造服务返回类型。

失败规则 ​

  • abort-product:关键启动插件失败时终止产品启动并回滚已激活插件;
  • skip-plugin:记录错误并跳过该插件及所有硬依赖它的插件;
  • continue:只适用于无下游硬依赖的增强插件;
  • 每个插件必须设置 timeoutMs,超时后中止其 AbortSignal;
  • 任一插件失败都不得留下事件订阅、定时器或服务注册。

TypeScript 契约和 JSON Schema 位于 @nexa/component-contracts,是接口、数据库和 Runtime 的共同真相源。

当前落地范围 ​

第一阶段运行时已位于 products/tactics-sim-engine/src/runtime/plugins/,内置插件位于产品的 src/plugins/。当前已经实现计划与装配版本绑定、制品锁一致性、Host Contract、插件版本依赖、组件能力依赖、稳定拓扑排序、权限裁剪、超时、失败策略和逆序释放。

当前已落地十个内置插件:

  • viewer-host 是优先级最高的默认基础插件,绑定 earth-viewer 生命周期,通过 viewer-host Capability 完成 Viewer 能力初始化、场景恢复与释放,并发布 viewer.ready;
  • spatial-environment 在 Viewer 就绪后通过空间环境 Capability 管理底图与地形,把纯快照和操作意图绑定到对应面板;面板与插件均不持有 Viewer;
  • entity-selection 接管实体选择联动:基础组件只发布 entity.selected,插件归一化选择状态,直接绑定 entity-props 的 Props 和 update:entity 事件,并通过数据端口回写实体;
  • schema-form-routing 依赖实体选择插件,根据场景节点和 Action 选择在元数据、时钟、动作三种表单模式间切换,直接绑定 schema-form 的 Schema、模型、上下文和更新事件。
  • workspace-interaction 只绑定场景树和实体列表,负责工作区选择传递;
  • camera-workflow 通过 Camera Capability 完成快照捕获、飞行、漫游和停止,插件不获取 Cesium Viewer;
  • scene-workflow 接管场景生命周期命令;
  • entity-workflow 接管实体创建及实体删除时的 Action 渲染清理;
  • action-workflow 接管活动创建、删除及其渲染清理;
  • timeline-orchestration 绑定时间轴数据和选择事件,并在 Viewer 就绪时启动时间执行同步。

面板拖拽和重挂载不再创建或销毁这些业务订阅。插件启用数据来自产品内置 Manifest 注册表,不依赖远程契约或产品装配文档。

上述插件通过 context.data 访问 scenario.entities、scenario.actions、scenario.metadata 和 scenario.clock。宿主的数据源注册器将这些语义化端口映射到 Pinia/Cesium,插件既不导入 Store,也不持有 Viewer,并会校验所有 unknown 返回值、丢弃过期异步结果以及在卸载后停止写入状态。

entityPropsAdapter.ts、schemaFormAdapter.ts、cameraPanelAdapter.ts、timelinePanelAdapter.ts、earthViewerAdapter.ts 与 remoteComponentAdapters.ts 已删除。RemoteRendererHost 只合并组件声明 Props 与插件绑定,不识别 Earth Viewer,也不执行组件专属初始化。后续组件迁移应扩展稳定 Capability Port 或数据源,再由插件编排,不再建立“一组件一个 Adapter”。

当前内置插件不使用配置。带 configSchema 的远程插件在接入插件注册表和 JSON Schema 校验器之前不得启用;数据端口也会拒绝访问尚未注册的数据源。