Files
Endoleap/src/lib/canvas-runtime/contracts.ts
T

96 lines
8.8 KiB
TypeScript

/**
* 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<Id,import('./asset-depot.ts').SharedAssetBinding>;
/** 分段读取过程中保留的 canonical 捕获提案,与已有 ACK 基线分开协调。 */
readonly loadingRead?:CanonicalProposal;
/** 当前流式加载覆盖范围/进度与发送门禁事实。 */
readonly loading?:import('./loading.ts').LoadingState;
/** 孤儿文件盘点/保留事实,不因当前 DOM 未展示就立即删除。 */
readonly orphanFiles?:ReadonlyMap<Id,OrphanFile>;
/** 导入与复制 job 的可恢复进度,包含源身份/游标/稳定映射,任务不归 view 所有。 */
readonly imports?: ReadonlyMap<Id, ImportJob>;
/** 唯一逻辑操作/冻结请求/结果事实;queue 和到期 heap 由它派生调度。 */
readonly ledger: LedgerSnapshot;
/** 已受理/控制/产生输出的生成任务记录,与文档元素和外部任务 ID 分开。 */
readonly runs: ReadonlyMap<Id, RunRecord>;
/** 最新已确认模型基线;后续本地编辑叠加在 working,不能用旧 ACK 直接覆盖。 */
readonly acknowledged: Document;
/** 每个参与 canvas 的服务端版本,用于条件提交;不是 rootRevision 或组件 dataVersion。 */
readonly versions: ReadonlyMap<Id, number>;
/** Raw recovery inputs, or adapter-owned summaries for fully reconciled document requests. */
/** 原始恢复输入或已完全协调请求的精简回执,保留外部执行证据。 */
readonly receipts: ReadonlyMap<Id, Json>;
/** 待处理冲突原因,不能从请求队列为空推断冲突已消失。 */
readonly conflicts: ReadonlyMap<Id, string>;
/** 文件/远端资产的逻辑资源状态,不把临时 blob URL 当可恢复身份。 */
readonly resources: ReadonlyMap<Id,ResourceRecord>;
/** 显式保留 owner→资源 ID 列表(如 recovery);working/history/ledger 的反向 pin 表由扫描派生。 */
readonly resourcePins: ReadonlyMap<Id,readonly Id[]>;
/** 用户已审阅的恢复/采纳证据,用于后续重放与审计,避免再次猜测冲突取舍。 */
readonly recoveries: ReadonlyMap<Id,RecoveryAudit>;
};
/** core Snapshot 的 runtime 具象化,单次订阅可同时看到模型和对应运行事实。 */
export type EngineSnapshot = Snapshot<RuntimeState>;
/** 服务 codec 提供的文档/版本候选,按合同应已校验;类型没有品牌或运行时校验,任意外部对象仍需边界验证。
* 不包含 history/ledger/Root revision,也不自动证明完整捕获或当前授权;协调完成后才可作为 ACK 基线。 */
export type CanonicalProposal = { readonly document: Document; readonly versions: ReadonlyMap<Id, number> };
/** 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<Id,number>; readonly versions:ReadonlyMap<Id,number>; 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<Id, number> };
/** 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<Id,number>; 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<CanonicalProposal>;
/** 从原请求与回执提取历史执行证明,供 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<OperationStatus>;
/** 查询原请求的执行状态;unknown/404 的含义由协议结果处理,不能擅自当 not-applied。 */
query(request: FrozenRequest, signal: AbortSignal): Promise<OperationStatus>;
/** 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;
};