Loro Rust 核心 crate 合并计划:将 `loro-internal` 并入 `loro` 单一 crate 的七阶段迁移指南
后端【免费下载链接】loroMake your JSON data collaborative and version-controlled with CRDTs项目地址https://gitcode.com/gh_mirrors/lo/loro点击查看免费下载导读本文档深入剖析 Loro 仓库中的核心工程迁移计划plans/20260306-merge-loro-crates.md将当前拆分为crates/loro公共门面与crates/loro-internal引擎实现的双 crate 架构合并为一个唯一发布的 Rust crateloro。文中完整呈现了从行为基线锁定、引擎导入、语义合并、表面塌缩到 WASM 消费者迁移、移除 shim 的七个阶段每个阶段均给出目标、范围、工作项、退出标准、验证命令与风险并结合仓库源码crates/loro/Cargo.toml、crates/loro/src/lib.rs、crates/loro/src/event.rs、crates/loro-internal/src/lib.rs、crates/loro-wasm/Cargo.toml等验证了现状描述与每项验证命令的可执行性。读完本文你将理解该计划为何按此顺序推进、每个阶段如何定义完成以及如何用cargo test、cargo bench和全仓搜索来守卫生成过程中不引入语义回退。说明该计划文档状态为Draft2026-03-06所有阶段状态均为Not Started本文是对其完整内容的展开与源码佐证不构成对已实施变更的描述。背景双 crate 拆分带来的两类问题当前 Loro 的 Rust 实现被拆分为两个 crate见仓库 crates/loro/Cargo.toml 与 crates/loro-internal/Cargo.tomlcrates/loro面向公众、有文档、语义稳定的门面facadecrate。它通过 path 依赖直接引用loro-internal { path ../loro-internal, version 1.16.2 }。crates/loro-internal承载绝大部分实现的引擎 crate其 Cargo.toml 描述本身就写着Loro internal library. Do not use it directly as its not stable.内部库请勿直接使用它不稳定。这种拆分产生两类问题重复的 API 层与类型表面门面 crate 围绕引擎类型重新包装出一套公开类型两套类型并存。层间转换开销门面与引擎之间反复做不必要的转换。计划明确指出成本最高的转换并不在于外层LoroDoc包装本身而集中在crates/loro/src/event.rs中的事件桥接event bridgingValueOrHandler到ValueOrContainer的转换Diff与DiffBatch的重新物化re-materialization围绕内部 handler 的容器句柄重新包装container handle re-wrapping。以 crates/loro/src/event.rs 为例源码可以验证这一判断DiffEventa通过FromDiffEventInnera桥接、ContainerDiffa通过Froma ContainerDiffInner桥接、Diffa通过Froma DiffInner桥接——每个事件触发时list diff 中的每个插入值都要执行ValueOrContainer::from(v.clone())重新包装。这正是计划要消除的 facade-onlyDiffEventreconstruction。拆分带来的长期维护成本还包括公开语义在一个 crate、引擎行为在另一个 crate而第一方消费者如crates/loro-wasm直接依赖低层内部 API。因此仅物理搬移文件而不塌缩重复类型层只能降低组织复杂度无法消除主要运行时开销。目的与目标状态计划的目标是让loro成为核心实现唯一对外发布的 Rust crate同时保持正确性并把迁移风险控制在可管理范围。目标状态target state为唯一规范实现 crateloro唯一规范LoroDoc唯一规范的 container / value / diff / event 表面没有任何第一方 crate 依赖loro-internal热路径上不再存在重复的 facade-to-engine 转换除非是有意保留的兼容 shim。Goals目标将crates/loro与crates/loro-internal合并为一个规范的 Rust crate。保持用户依赖的现有公开语义尤其是auto-commit 默认行为。移除对热路径有实质性影响的重复类型层。迁移期间工作区workspace始终保持可构建。每个迁移阶段都给出明确的进入条件entry criteria、交付物deliverables、退出标准exit criteria与验证步骤validation。Non-Goals非目标重写 CRDT 引擎。在合并期间重新设计无关 API。在第一轮迭代中移除所有逃生舱escape hatch。改变 JS 与 WASM 行为除非 Rust 合并本身要求如此。追求无收益的纯文件搬移——除非能带来可衡量的简化或性能收益。硬约束Hard Constraintsloro::LoroDoc::new()必须保持当前 auto-commit 行为除非有显式的公开 API 决策另有规定。导入/导出import/export、diff、checkout、undo、订阅subscription的正确性不得回退。loro-wasm的 pending-event flush 不变量必须保持有效。工作区在每一个阶段边界都必须可构建。性能敏感变更必须用针对性 benchmark 验证而不能只看编译是否通过。成功指标Success Metricscrates/loro不再依赖loro-internal。没有任何第一方 crate importloro-internal。第一方热路径不再需要当前的 facade-onlyDiffEvent重建。异构读取路径heterogeneous read paths在规范 API 上不再需要重复的ValueOrHandler→ValueOrContainer转换。crates/loro/tests仍然是有效的公开兼容测试套件。公开 crate 文档继续随loro发布而不是随隐藏的引擎 crate 发布。现状摘要Current-State Summary含源码佐证计划文档给出的现状如下仓库源码均可逐一印证crates/loro/src/lib.rs用公开门面类型包装InnerLoroDoc与内部 handlers。源码第 100-106 行可看到pub struct LoroDoc { doc: InnerLoroDoc, ... }其中InnerLoroDoc即loro_internal::LoroDoccrates/loro/src/lib.rsnew()的实现在创建InnerLoroDoc::default()后调用doc.start_auto_commit()crates/loro/src/lib.rs——这正是auto-commit 默认行为的源码落点也是 Phase 2 必须守护的语义。crates/loro/src/event.rs从内部表示重建 event、diff、batch 类型。前面已列举From桥实现crates/loro/src/event.rs包含ListDiffItem::Insert中对每个值执行ValueOrContainer::from(v.clone())的重复物化。crates/loro-internal/src/lib.rs暴露的引擎表面远超应成为长期公开契约的范围。从 crates/loro-internal/src/lib.rs 可见其顶层公开了大量模块arena、diff、diff_calc、handler、sync、oplog、encoding、container、cursor、dag、jsonpath、kv_store、txn、version、undo、pre_commit、estimated_size等以及LoroDoc/LoroDocInner内部由oplog、state、arena、config、txn、auto_commit、detached等字段构成见 crates/loro-internal/src/lib.rs。crates/loro-wasm大量导入loro-internal的低层项因此合并必须包含消费者迁移计划而不仅是 crate 搬移。这一点在依赖与源码两处都有直接证据crates/loro-wasm/Cargo.toml 直接声明loro-internal { path ../loro-internal, features [wasm, counter, jsonpath] }对crates/loro-wasm/src的检索显示大量loro_internal引用convert.rs11 处、lib.rs7 处、awareness.rs/container_tree.rs/counter.rs各 1 处。阶段总览Tracking Dashboard计划用一张跟踪仪表盘管理七个阶段每个阶段标记状态Not Started/In Progress/Blocked/Done、依赖关系与主要产出Phase名称状态依赖主要产出0Lock behavior and perf baselineNot Started无基线测试与 benchmark 数字1Import engine intoloroNot StartedPhase 0loro拥有实现模块2Merge canonicalLoroDocsemanticsNot StartedPhase 1唯一规范文档类型3Collapse container and value surfaceNot StartedPhase 2唯一规范 container/value 层4Collapse event, diff, and undo surfaceNot StartedPhase 3唯一规范 event/diff/undo 层5Migrateloro-wasmand first-party consumersNot StartedPhase 4第一方不再依赖loro-internal6Remove shim and finalize cleanupNot StartedPhase 5单 crate 稳态文档使用方式How to Use This Document随工作推进更新每个阶段的状态Not Started、In Progress、Blocked或Done。每个合并后的 PR 应同步更新本文档中相关的 checklist 项。只有当某阶段的所有退出标准都满足时该阶段才算Done。如果重大设计决策改变了计划在继续之前先更新 Decision Log 一节。Phase 0锁定行为与性能基线状态Not Started目标Objective在改变 crate 边界之前冻结当前公开行为并记录基线性能。为什么需要这一阶段Why This Phase Exists如果没有基线后续阶段可能在编译依然通过的情况下意外改变语义。本阶段把当前行为变成一份显式契约。主要范围Primary Scope经由crates/loro验证的公开 Rust 行为当前由crates/loro-internal覆盖的引擎正确性受 facade-to-engine 转换影响的性能敏感路径。工作项Work Items盘点当前仅由crates/loro提供的公开语义。将crates/loro/tests视为公开兼容套件。确定crates/loro-internal/tests中必须在整个迁移期间保持绿色green的最小测试子集。增加或确认以下方向的 benchmark 覆盖active subscriptions活跃订阅heterogeneous reads异构读取diff / apply-diff 路径undo callbacks撤销回调记录基线命令并在 PR 或关联产物中保存基线数字。交付物Deliverables一份书面基线总结一份稳定的兼容测试清单关键热路径的 benchmark 数字。退出标准Exit Criteria公开语义已被枚举并就绪enumerated and agreed upon。所需测试与 benchmark 已识别且可运行。已在当前拆分架构上完成一次基线测量。验证Validation计划给出的验证命令如下与仓库现状一致cargo test -p lorocargo test -p loro-internalcargo bench -p loro-internal eventcargo bench -p loro-internal pendingcargo bench -p loro-internal list其中event、pending、list三个 bench 目标在 crates/loro-internal/Cargo.toml 的[[bench]]声明中确实存在完整的 bench 列表还包括text_r、encode、map、tree、jsonpath、shallow_export。风险Risks漏掉某个公开语义边界情况日后把它误当作实现细节处理测错路径、优化错层。Phase 1将引擎导入loro状态Not Started目标Objective在保持当前公开行为的前提下让loro拥有实现模块。策略Strategy推荐策略是先把实现搬进crates/loro再在后续阶段削减重复表面。这样既保持发布的 crate 名称稳定又避免在同一步内做大规模语义重写。主要范围Primary Scopecrates/loro/Cargo.tomlcrates/loro/src/**crates/loro-internal/Cargo.tomlcrates/loro-internal/src/**工作项Work Items在crates/loro内创建内部模块树以承载当前引擎实现。合并crates/loro与crates/loro-internal的依赖集合。合并 feature flags同时保留公开的lorofeature 契约。让crates/loro直接对本地引擎模块编译而不是通过指向loro-internal的 path 依赖。将crates/loro-internal转换为临时兼容 shim从loro重新导出。在下游消费者仍在迁移期间通过 shim 保持工作区可构建。关于 feature flags 的现状当前loro的 feature 契约是default [counter]、counter [loro-internal/counter]、jsonpath [loro-internal/jsonpath]、logging [loro-internal/logging]crates/loro/Cargo.toml而loro-internal自身还有wasm、test_utils等 featurecrates/loro-internal/Cargo.toml。合并时必须保证这些透传关系在loro内部自洽这正是合并 feature flags 同时保留公开 feature 契约的难点所在。交付物Deliverablesloro在没有指向loro-internal的 path 依赖的情况下完成构建loro-internal仍作为临时转发 crate 存在尚无有意的公开行为变更。退出标准Exit Criteriacargo tree -p loro不再显示loro-internal依赖边。公开测试仍指向loro并保持绿色。兼容 shim 足以让第一方 crate 继续构建。验证Validationcargo test -p lorocargo test -p loro-internalworkspace 构建检查风险Risks导入循环或意外的 feature 漂移把过多公开表面搬进loro的根。Phase 2合并规范LoroDoc语义状态Not Started目标Objective移除LoroDoc { doc: InnerLoroDoc }这种外层拆分让唯一的规范LoroDoc类型拥有合并后的行为。主要范围Primary Scope文档构造函数document constructorsauto-commit 行为fork、fork_at、from_snapshot容器的doc()行为公开逃生舱escape hatch如inner()、with_oplog、with_state这些项在源码中都有对应物fork_atcrates/loro/src/lib.rs、from_snapshotcrates/loro/src/lib.rs、with_oplog/with_statecrates/loro/src/lib.rs / crates/loro/src/lib.rs、inner()返回InnerLoroDoccrates/loro/src/lib.rs各容器 handler 的doc()实现分布在 lib.rs 多处如第 1805、2098、2405、2916、3330、3628、3752 行。合并后这些逃生舱的存废将进入显式决策。工作项Work Items将当前公开构造函数语义搬入规范的合并LoroDoc。为以下路径保持当前 auto-commit 行为new()from_snapshot()fork_at()从已挂载容器返回的doc()决定inner()是保留、弃用还是被更窄的 API 替代。决定with_oplog与with_state的长期形态。确保第一方构造函数如loro-wasm中的保持相同行为。交付物Deliverables唯一规范LoroDoc文档生命周期 API 无公开行为回退。退出标准Exit Criteria公开LoroDoc不再是对第二种文档类型的门面。所有已知 auto-commit 语义与基线一致。逃生舱行为被显式文档化而非偶然存在。验证Validationcargo test -p loro针对以下项的定向测试new()from_snapshot()fork_at()已挂载容器的doc()风险Risks手动提交manual-commit与自动提交auto-commit行为之间出现静默漂移意外把低层锁或状态 API 拓宽为永久公开契约。Phase 3塌缩容器与值表面Container and Value Surface状态Not Started目标Objective移除门面围绕内部 handlers 与值枚举重复包装的容器层与值层。主要范围Primary ScopeLoroList、LoroMap、LoroText、LoroTree、LoroMovableList、LoroCounterContainerValueOrContainerContainerTraithandler-to-container 与 value-to-value 的转换路径工作项Work Items为容器句柄选择规范命名策略。决定是否将LoroText等公开名称保留为别名alias、重命名后的规范类型还是兼容包装器。在切实可行处将Container与内部 handler 枚举塌缩为一个规范表示。在切实可行处将ValueOrContainer与ValueOrHandler塌缩为一个规范表示。如果ContainerTrait仅用于桥接两个类型层则移除或收窄它。消除以下路径中转换密集的读取list / map gettersfor_eachvaluesget_by_pathget_by_str_pathjsonpath交付物Deliverables唯一规范的容器句柄层唯一规范的 value-or-container 层读取密集路径上的包装开销wrapper churn显著减少。退出标准Exit Criteria规范 API 不再需要当前重复的 handler-to-container 与 value-to-value 包装。公开名称稳定或被显式地兼容 shim 化。loro中已存在规范容器类型的文档。验证Validationcargo test -p loro读取路径回归测试与 Phase 0 异构读取基线对比的 benchmark风险Risks公开类型推断type inference发生变化若别名选择不当会丢失易用的名称或公开文档。Phase 4塌缩事件、diff 与 undo 表面状态Not Started目标Objective移除当前的事件与 diff 重建层——这是合并中价值最高的运行时简化。为什么单独成阶段Why This Phase Is Separate这是最敏感的 API 表面。内部与公开的事件形态今天并不相同因此本阶段需要显式决策而不是隐式重构。源码可佐证这一点crates/loro/src/event.rs中的DiffEventa、ContainerDiffa、Diffa、ListDiffItem、MapDeltaa均从loro-internal的内部类型DiffEventInner、ContainerDiffInner、DiffInner、ValueOrHandler、ResolvedMapDelta等经From转换而来crates/loro/src/event.rs。主要范围Primary Scopesubscriptions订阅DiffEventContainerDiffDiffDiffBatchundo 回调载荷callback payloads决策门Decision Gate在开始实现之前必须从以下方案中选择一种并记入 Decision Log兼容优先Compatibility-first先引入规范化的借用borrowed或原始raw事件 API把当前 owned 事件 API 作为兼容层保留一个或多个版本。立即破坏Break-now在 semver-major 变更中立即替换旧事件形态。工作项Work Items选择规范事件模型。选择规范 diff 模型。决定旧subscribe是否暂时保留为兼容包装器。更新UndoManager回调载荷以使用规范事件或 diff 类型。移除第一方热路径对 facade-only 事件重建的依赖。重跑活跃订阅 benchmark 并与 Phase 0 基线对比。交付物Deliverables唯一规范的事件与 diff 表面事件路径的分配或转换工作量可测量地下降。退出标准Exit Criteria第一方热路径不再需要当前的DiffEvent::from桥。undo 回调不再需要重复的事件模型。所选兼容策略被文档化并被强制执行。验证Validationcargo test -p loro订阅聚焦的回归测试与 Phase 0 活跃订阅基线的 benchmark 对比风险Risks若借用式事件 API 公开会带来公开生命周期lifetime复杂度若新旧事件 API 共存过久会带来兼容开销。Phase 5迁移loro-wasm与其他第一方消费者状态Not Started目标Objective移除第一方对loro-internal的依赖让loro成为工作区消费者唯一使用的 crate。主要范围Primary Scopecrates/loro-wasmexamples示例benches基准仍依赖loro-internal的内部工具或支撑 crate工作项Work Items将crates/loro-wasm的 import 从loro-internal切换到loro。若仍需要低层支持从loro暴露一个窄范围narrowly scoped的内部支持模块而不是暴露整个引擎根。保持 JS pending-event flush 不变量完好。审计所有工作区成员并移除对loro-internal的直接 import。更新 examples 与 benches 以使用合并后的 crate 表面。如现状摘要所述crates/loro-wasm当前在 Cargo.toml 中直接依赖loro-internal含wasm、counter、jsonpath三个 feature并在convert.rs、lib.rs、awareness.rs、container_tree.rs、counter.rs中直接引用loro_internal符号因此本阶段需要逐文件替换 import并处理loro-internal的wasmfeatureUTF-16 索引与 wasm-bindgen 支持如何在合并后继续生效的问题。交付物Deliverables没有任何第一方 crate 直接依赖loro-internalloro-wasm基于loro构建。退出标准Exit Criteria仓库级搜索显示不存在第一方对loro-internal的 import临时 shim crate 本身除外。loro-wasm行为保持正确。任何隐藏的内部支持表面都被有意地划定范围并文档化。验证Validationcargo test -p loro-wasmpnpm -C crates/loro-wasm build-release仓库级搜索loro_internal风险Risks为了迁就loro-wasm不小心把过多引擎 API 提升为长期公开表面在改动绑定代码时破坏事件 flush 不变量。Phase 6移除 shim 并完成清理状态Not Started目标Objective删除临时兼容 crate完成向真正单 crate 架构的过渡。主要范围Primary Scopecrates/loro-internaldocs文档readmesrelease notes发布说明migration notes迁移说明工作项Work Items删除crates/loro-internal。移除不再服务于迁移目的的任何临时兼容别名或转发代码。将剩余测试、bench 与文档合并进lorocrate。更新 crate 文档与仓库文档。若任何用户可见 API 发生移动或变更编写发布说明与迁移指南。交付物Deliverables工作区中不再存在loro-internalcrate实现与公开文档的唯一事实来源single source of truth。退出标准Exit Criteria工作区在没有loro-internal的情况下可编译并通过测试。文档只引用合并后的 crate 结构。若发生任何兼容性破坏迁移指南已就绪。验证Validationworkspace 构建与测试通过仓库级搜索确认不再有loro-internal引用历史 changelog 文本除外。风险Risks过早删除 shim在文档、脚本或示例中遗留过期的内部引用。跨阶段风险Cross-Phase Risks事件形态兼容可能主导整个排期如果不在早期做出决策DiffEvent的形态兼容问题会拖慢所有后续阶段。loro-wasm可能迫使暴露比预期更宽的内部支持表面低层依赖与 wasm feature 的迁移需求会反推loro的公开面。构造函数语义可能回退如果合并时把loro-internal::LoroDoc::new()视为与当前公开loro::LoroDoc::new()等价就会引入语义回退两者当前并不等价——公开门面的new()显式调用了start_auto_commit()见 crates/loro/src/lib.rs。公开文档质量可能回退如果内部类型在文档迁移完成之前就变成规范类型。开放问题Open Questions合并是否允许对事件层做一次 semver-major 的 Rust API 清理为loro-wasm暴露的隐藏内部支持模块确切名称应该是什么合并后哪些逃生舱应保持公开哪些应弃用规范容器类型名称应保持LoroText/LoroList/LoroMap还是应围绕当前内部 handler 名称重命名规范 API 引入后兼容包装器应保留多久决策日志Decision Log目前尚无决策记录。建议的 PR 顺序Suggested PR Sequencetest(loro): lock behavior and perf baselinerefactor(loro): import internal engine into public craterefactor(loro): merge canonical LoroDoc semanticsrefactor(loro): collapse container and value surfacerefactor(loro): collapse event, diff, and undo surfacerefactor(wasm): migrate first-party consumers to lororefactor(loro): remove internal shim and finalize cleanup完成定义Definition of Done当以下所有条件为真时本计划才算完成loro是核心实现唯一的 Rust crate。loro-internal已被移除。第一方消费者使用loro。规范的事件 / value / container / doc 表面不再需要热路径上的门面转换层。公开语义保持正确并被文档化。附在仓库中深入阅读的入口计划全文plans/20260306-merge-loro-crates.md门面 crate 清单与依赖crates/loro/Cargo.toml可见loro-internalpath 依赖与 feature 透传门面LoroDoc包装与构造函数crates/loro/src/lib.rs事件重建桥crates/loro/src/event.rs引擎 crate 的公开表面crates/loro-internal/src/lib.rs引擎LoroDocInner内部结构crates/loro-internal/src/lib.rsWASM 消费者依赖crates/loro-wasm/Cargo.toml引擎 benchmark 声明验证命令对应目标crates/loro-internal/Cargo.toml赞分享后端【免费下载链接】loroMake your JSON data collaborative and version-controlled with CRDTs项目地址https://gitcode.com/gh_mirrors/lo/loro点击查看免费下载相关推荐Loro 内部 CRDT 核心 crateloro-internal开发指南模块地图、核心不变量与验证命令Loro 内部 CRDT 核心 crateloro internal开发指南模块地图、核心不变量与验证命令 本篇技术指南以 crates/loro int后端Loro 内部实现开发指南loro-internal crate 的模块地图、验证命令与核心不变量Loro 内部实现开发指南loro internal crate 的模块地图、验证命令与核心不变量 本文是一份面向 Loro 仓库贡献者与深入研究者的 lor后端Loro冲突解决机制理解CRDT如何自动合并并发修改Loro冲突解决机制理解CRDT如何自动合并并发修改 在当今协作应用盛行的时代 Loro冲突解决机制 为开发者提供了一个革命性的解决方案。想象一下当多个用后端上一篇PrismLauncher-Cracked终极Minecraft离线启动解决方案指南下一篇KMS_VL_ALL_AIO终极指南Windows和Office永久激活的简单免费解决方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考