/** * core 与 runtime/service 的共享合同:RuntimeState 与文档/历史/操作一起由一个 Root 发布。 * ledger 是调度事实源,acknowledged+versions 是已确认服务基线,working 文档仍位于 EngineSnapshot.document。 * 外部异步 read/submit/query 只能形成提案,由 engine/reconciler 校验后统一发布,不直接覆盖 working。 */ import type {OrphanFile} from './orphan-files.ts'; import type { ImportJob } from './imports.ts'; import type { CanvasRecord, ElementRecord, Document, Id, Json, OperationIntent, Snapshot } from '../canvas-core/types.ts'; import type { LedgerSnapshot, FrozenRequest } from './ledger.ts'; import type { CompiledRequest, OperationStatus } from './scheduler.ts'; import type { RunRecord } from './runs.ts'; import type { ResourceRecord } from './resources.ts'; import type { RecoveryAudit } from './conflicts.ts'; /** 与 core 文档/历史同 Root 发布的运行事实;view/selection/gesture 和渲染缓存不在此持久状态中。 * 这是类型合同,不执行输入校验/序列化或深冻结;Map/记录的实际捕获由 engine、codec 和生产模块负责。 * 内存 Root 的变化不等于磁盘 durable 切点,调度仍需存储确认与 durableGeneration/rootRevision 等屏障。 */ export type RuntimeState = { /** 项目资源与共享 AssetDepot 的授权/身份绑定,非第二份图片字节。 */ readonly assetBindings?:ReadonlyMap; /** 分段读取过程中保留的 canonical 捕获提案,与已有 ACK 基线分开协调。 */ readonly loadingRead?:CanonicalProposal; /** 当前流式加载覆盖范围/进度与发送门禁事实。 */ readonly loading?:import('./loading.ts').LoadingState; /** 孤儿文件盘点/保留事实,不因当前 DOM 未展示就立即删除。 */ readonly orphanFiles?:ReadonlyMap; /** 导入与复制 job 的可恢复进度,包含源身份/游标/稳定映射,任务不归 view 所有。 */ readonly imports?: ReadonlyMap; /** 唯一逻辑操作/冻结请求/结果事实;queue 和到期 heap 由它派生调度。 */ readonly ledger: LedgerSnapshot; /** 已受理/控制/产生输出的生成任务记录,与文档元素和外部任务 ID 分开。 */ readonly runs: ReadonlyMap; /** 最新已确认模型基线;后续本地编辑叠加在 working,不能用旧 ACK 直接覆盖。 */ readonly acknowledged: Document; /** 每个参与 canvas 的服务端版本,用于条件提交;不是 rootRevision 或组件 dataVersion。 */ readonly versions: ReadonlyMap; /** Raw recovery inputs, or adapter-owned summaries for fully reconciled document requests. */ /** 原始恢复输入或已完全协调请求的精简回执,保留外部执行证据。 */ readonly receipts: ReadonlyMap; /** 待处理冲突原因,不能从请求队列为空推断冲突已消失。 */ readonly conflicts: ReadonlyMap; /** 文件/远端资产的逻辑资源状态,不把临时 blob URL 当可恢复身份。 */ readonly resources: ReadonlyMap; /** 显式保留 owner→资源 ID 列表(如 recovery);working/history/ledger 的反向 pin 表由扫描派生。 */ readonly resourcePins: ReadonlyMap; /** 用户已审阅的恢复/采纳证据,用于后续重放与审计,避免再次猜测冲突取舍。 */ readonly recoveries: ReadonlyMap; }; /** core Snapshot 的 runtime 具象化,单次订阅可同时看到模型和对应运行事实。 */ export type EngineSnapshot = Snapshot; /** 服务 codec 提供的文档/版本候选,按合同应已校验;类型没有品牌或运行时校验,任意外部对象仍需边界验证。 * 不包含 history/ledger/Root revision,也不自动证明完整捕获或当前授权;协调完成后才可作为 ACK 基线。 */ export type CanonicalProposal = { readonly document: Document; readonly versions: ReadonlyMap }; /** Receiving records are read deltas, not a validated Document or an ACK base. */ /** receiving 为尚未完成的 delta:头部一次声明 canvas/墓碑/可选范围证据,后续按读取协议增加元素。 * received 可包括依赖未闭合、尚不能发布的元素;不是完整 Document、ACK 或可直接提交给 editor 的 working。 * CanonicalReadUpdate 不携带网络 query/token/sequence 的完整围栏,那些由外层 LoadChunk 绑定。 */ export type CanonicalReadUpdate = CanonicalReadResult | { readonly status:'receiving'; readonly canvasElementCounts?:import('./scope-counts.ts').CanvasElementCounts;readonly elementOwners?:import('./scope-counts.ts').ElementOwners; readonly projectId:Id; readonly canvases:readonly CanvasRecord[]; readonly elements:readonly ElementRecord[]; readonly tombstones:ReadonlyMap; readonly versions:ReadonlyMap; readonly receivedElements:number; readonly totalElements:number }; /** ready 提供新模型;migration-required 描述显式迁移资格,不在读取时偷偷执行迁移。 */ export type CanonicalReadResult = ({ readonly status: 'ready';readonly sourceToken?:string } & CanonicalProposal) | { readonly status: 'migration-required'; readonly rootId: Id; readonly sourceVersions: ReadonlyMap }; /** A validated historical execution receipt; its old model is never published over a fresh read. */ /** 由请求解释器验证历史 applied 回执后构造:versions 表示该次写入下界,assertCanonical 检查 fresh 内容兼容。 * proof 不包含待发布旧模型,也不持久化函数;恢复时由原请求/回执重新解释,回调应纯且基于不可变证据。 * 证明服务执行不等于本地已协调/已保存,更不授权再提交相同变化。 */ export type AppliedModelProof = { readonly versions:ReadonlyMap; assertCanonical(proposal:CanonicalProposal):void }; /** 服务适配合同:compile/canonical/verifyApplied 等同步方法负责纯编解码/验证,异步方法负责外部 I/O。 * 引擎拥有本地发布/重放/持久化顺序;adapter 不应直接修改 snapshot、history 或 ledger。 * AbortSignal 只表达取消要求,不证明远端操作没生效;任何失败/超时都须按原 request 身份处理。 * 接口声明不会自动执行这些规则,具体实现和协调入口必须分别校验。 */ export type PersistencePort = { /** Complete canonical read for a quiescent recovery session; never a legacy fallback. */ /** 恢复协调所需的完整 canonical read;不能拿 legacy fallback 当目标协议基线。 */ read?: (signal: AbortSignal) => Promise; /** 从原请求与回执提取历史执行证明,供 receipt 过期/旧模型协调使用。 */ verifyApplied?: (request:FrozenRequest,receipt:Json) => AppliedModelProof; /** 判断是否有原冻结协议解释器,不支持时保留原 body/key 并阻塞,不能重编码换身份。 */ supportsRequest?: (request: FrozenRequest) => boolean; /** 从稳定 intent/requestId/当前快照计算参与范围、expected versions 与待冻结 payload,不执行网络写入。 * scheduler/ledger 随后冻结字节并过持久屏障;重试已经冻结的请求应复用原 body,不以最新快照重编。 */ compile(intent: OperationIntent, requestId: Id, snapshot: EngineSnapshot): CompiledRequest; /** Compile an ordered mutable batch; logical IDs and original intents remain independent. */ /** 可把有序未冻结意图合并为一份 wire 请求,原 logical IDs 与意图事实仍独立保留。 */ compileBatch?: (intents: readonly OperationIntent[], requestId: Id, snapshot: EngineSnapshot) => CompiledRequest; /** 发送原 FrozenRequest,保持已冻结字节/key;中断或丢响应不等于服务端没执行。 */ submit(request: FrozenRequest, signal: AbortSignal): Promise; /** 查询原请求的执行状态;unknown/404 的含义由协议结果处理,不能擅自当 not-applied。 */ query(request: FrozenRequest, signal: AbortSignal): Promise; /** Optional pure codec for a joint resolved publication. Rejection/failure retains the raw recovery input. */ /** 可选精简回执 codec,仅在联合已协调发布时采用;失败应保留原恢复输入。 */ compactResolvedReceipt?: (request:FrozenRequest,receipt:Json,applied:CanonicalProposal)=>Json; /** Pure codec, merging only participants into the latest base, never an old working snapshot. */ /** 解释原请求/结果,对最新 acknowledged 合并参与者并返回提案,不能拿旧 working 覆盖本地新编辑。 * 不自行发布 ACK 或重放 pending;引擎还需校验候选与版本,重建 working 后统一发布。 */ canonical(request: FrozenRequest, status: OperationStatus, snapshot: EngineSnapshot): CanonicalProposal; };