跳到主要内容

WorkingTreeManager

Defined in: packages/rxdb-plugin-working-tree/src/working-tree/working-tree-facade.ts:127

工作树与提交历史的入口(契约见 contracts/core-api.md §1)。

Remarks​

实例在 use(rxDBPluginWorkingTree) 的那一刻建好(插件构造器里),与库是否启用提交能力 无关——「有没有这个入口」是进程内库版本的属性,「能不能用」才是这个数据库的属性。 两者混在一起的话,同一份代码在两个库上会长出不同的对象形状。

抽包之后这条分工反而更硬:入口的存在与否变成「装没装 @aiao/rxdb-plugin-working-tree」, 仍是构建期属性,而没装该包时 database.workingTree 是编译错误而不是静默 undefined。 挂载因此必须留在插件构造器里,不能挪进 install()——后者跑在 init() 内部,会把入口 变成连接纪元的函数。

Constructors​

Constructor​

new WorkingTreeManager(rxdb): WorkingTreeManager;

Defined in: packages/rxdb-plugin-working-tree/src/working-tree/working-tree-facade.ts:141

由插件构造器调用,一个 RxDB 实例一个。

Parameters​

ParameterTypeDescription
rxdbRxDB宿主实例;整个门面只持有它这一个引用

Returns​

WorkingTreeManager

Remarks​

持整个 RxDB 而不是在这里就取出本地适配器:受管成员每次调用都要当场重新解析 (#runInTransaction 每次都走一遍 localAdapter$),因为 enable() 与重连都会换掉 适配器实例。构造那一刻取一次存下来,等于把门面钉死在第一个连接纪元上, 症状是重连之后所有命令仍然写向已经废弃的那个适配器。

Methods​

commit()​

commit(message, options): Promise<CommitResult>;

Defined in: packages/rxdb-plugin-working-tree/src/working-tree/working-tree-facade.ts:299

把当前分支工作树里的全部未提交单元提交进历史(FR-041)。

Parameters​

ParameterTypeDescription
messagestring提交消息
optionsCommitOptions见 CommitOptions;三个捕获位与作者、操作 id 全部必填

Returns​

Promise<CommitResult>

见 CommitResult;CAS 落败时是 ok: false 的返回值,不是异常

Throws​

WorkingTreeCapabilityDisabledError 这个库还没启用提交能力

Throws​

CommitGraphCorruptedError 当前分支的提交图已损坏(FR-051)

Throws​

CommitValidationError 消息为空、或工作树是干净的(empty_commit)

Remarks​

恰好两个位置参数:没有 selection 入参(硬裁决 1),也没有可选的第三参。 写 commit 与清空工作树在同一个事务里(FR-011、SC-007),而那个事务由 runEnabled 开——命令体自己不开事务,否则门禁读到的启用态与写入就分属两笔。


commitChanges()​

commitChanges(commitId): Promise<CommitChangeSetPage>;

Defined in: packages/rxdb-plugin-working-tree/src/working-tree/working-tree-facade.ts:362

读一个 commit 的全部变更单元(FR-012 的明细侧)。

Parameters​

ParameterTypeDescription
commitIdstring要读的 commit id

Returns​

Promise<CommitChangeSetPage>

见 CommitChangeSetPage;基线节点是没有变更单元的返回值,不是异常

Throws​

WorkingTreeCapabilityDisabledError 这个库还没启用提交能力

Throws​

RxDBError 这个 commit 不存在时

Remarks​

与 listCommits 的可达性口径不同:这里按 id 直读不可变快照行,不做可达性遍历。 调用方点的是它刚在可达历史里看到的节点;而一个悬挂 commit 的快照行读出来也是它写入 时的真实内容,不是编造的——「这个 commit 在不在当前分支的历史里」由列表侧回答, 明细侧只回答「它写了什么」。


diff()​

diff(options?): Promise<WorkingTreeDiff>;

Defined in: packages/rxdb-plugin-working-tree/src/working-tree/working-tree-facade.ts:277

当前分支相对 HEAD 的未提交改动(FR-005)。

Parameters​

ParameterTypeDescription
optionsWorkingTreeDiffOptions粒度、实体过滤与分页;全部可选

Returns​

Promise<WorkingTreeDiff>

见 WorkingTreeDiff

Throws​

WorkingTreeCapabilityDisabledError 这个库还没启用提交能力

Remarks​

只有一条 diff 轴:HEAD ↔ 工作树(硬裁决 2)。没有 from / to / ref 入参—— 那三个形参属于「比任意两个 ref」的模型,而 v1 没有 index,第二条轴无从谈起。

与 status 同样不收 branchId:读别的分支的未提交改动,拿回去既不能提交 也不能丢弃,三个捕获位只对 active 分支有效(FR-048)。


discard()​

discard(options): Promise<WorkingTreeDiscardResult>;

Defined in: packages/rxdb-plugin-working-tree/src/working-tree/working-tree-facade.ts:320

把当前分支的工作树整体退回当前 HEAD(FR-016)。

Parameters​

ParameterTypeDescription
optionsWorkingTreeCredentials见 WorkingTreeDiscardOptions;三个捕获位必填

Returns​

Promise<WorkingTreeDiscardResult>

见 WorkingTreeDiscardResult;干净工作树上是 discardedCount: 0 的 no-op

Throws​

WorkingTreeCapabilityDisabledError 这个库还没启用提交能力

Throws​

CommitGraphCorruptedError 当前分支的提交图已损坏(FR-051)

Remarks​

恰好一个必填位置参数。 做成可选的话,「缺省时由本次调用内部读取 revision」就成了 合法用法——内部读到的恒等于当前值,CAS 永远命中,FR-031 对 discard 的那半句当场失效。

参数本身不在这里校验:未启用的库该听到的是「去 enable()」,而不是 「expectedBranch 不能为空」——后者在这个库上根本无从谈起。


enable()​

enable(): Promise<CommitCapabilityInfo>;

Defined in: packages/rxdb-plugin-working-tree/src/working-tree/working-tree-facade.ts:184

启用这个数据库的提交能力(幂等)。

Returns​

Promise<CommitCapabilityInfo>

启用后的能力状态

Throws​

BranchNotMaterializableError 任一本地分支沿 rxdb_change 链物化不了时

Remarks​

「启用」是两件事,而且必须在同一个事务里:翻能力位,以及给每条本地分支补上根节点 (FR-021/049)。拆成两个事务的话,中间崩一次就停在「已启用、但分支没有根」——此后每次 commit() 都往一个无根分支上挂节点,而库自称一切正常。迁移抛错时整笔回滚,能力位一并 退回未启用,于是重试面对的还是同一个起点。

迁移每次都跑,不是只跑在 CAS 命中的那一次。 能力位的 CAS 只在 false → true 那一次 命中(幂等与并发仲裁都由它负责),但 enable() 的语义是「把库收敛到已启用该有的形状」, 而不是「翻一次位」:库启用之后才出现的本地分支——旧版本客户端建的、或上一次因某条分支 损坏而整体回滚的——只能靠再调一次 enable() 补根。真跑过一遍之后重复调用是幂等的, 全部分支都落进 alreadyInitializedBranchIds,一条语句都不发。

捕获运行时装在事务提交之后。 装在事务里的话,捕获会立刻开始拦截这同一个事务余下的 写——而 runEnableMigration() 往分支上补的根节点正是在那里面写的,于是「启用」这件事 自己会被记成一批未提交变更。装在提交之后,本进程从下一次写开始捕获;这一笔启用本身 属于 HEAD,不属于工作树。

迁移抛错时整笔回滚、能力位退回未启用,此时 transaction() 直接向上抛,装载那一行 走不到——不会留下「没启用却在捕获」的形状。


enableIfEmpty()​

enableIfEmpty(): Promise<WorkingTreeEnableIfEmptyResult>;

Defined in: packages/rxdb-plugin-working-tree/src/working-tree/working-tree-facade.ts:227

库为空时自动启用,已有内容时一行不写(应用启动时的自动初始化入口)。

Returns​

Promise<WorkingTreeEnableIfEmptyResult>

见 WorkingTreeEnableIfEmptyResult

Throws​

BranchNotMaterializableError 任一本地分支沿 rxdb_change 链物化不了时 (只发生在「空」判定通过、启用迁移开跑之后,与 enable 同形)

Remarks​

「空」的判据是 rxdb_change 行数为零。 本地每一次实体写入都会追加一条变更 (undo/redo 的数据源),sync pull 走 disableTriggers 不产生行;行数为零 ⟺ 这个库 从来没有过用户内容。判据不用用户实体行数:插件不认识应用注册的实体清单,按实体 逐张表数一遍等于把「什么是内容」摊给每个调用方。

判空、翻能力位、补 baseline 在同一个事务里。 拆开的话,判空之后、启用之前有别的 writer 落进第一批内容,自动启用就会把一份刚出现的用户数据折进 baseline——而这条规则 的全部意义就是「有内容时不替用户做这个决定」。

三种结局分工明确。 already_enabled 原样报告、不跑启用迁移:把库收敛到已启用 该有的形状是 enable 的语义(它的 TSDoc 承诺重复调用补根),这里只回答 「要不要启用」;not_empty 返回时能力位仍是关的,手动 enable() 面对的是同一个起点。 捕获运行时在两个启用结局之后照常装载,与 enable() 重复调用的幂等行为一致; not_empty 不装——本进程尚未启用,装了等于在未启用的库上开始捕获。

能力行缺失照常抛(readCommitCapability 自己抛),不按「未启用且空」继续: 那是 0004 迁移没跑完的损坏现场,自动启用会把损坏掩埋成一次正常启动。


isEnabled()​

isEnabled(): Promise<boolean>;

Defined in: packages/rxdb-plugin-working-tree/src/working-tree/working-tree-facade.ts:154

这个数据库启用提交能力了吗。

Returns​

Promise<boolean>

已启用返回 true

Remarks​

与 enable / enableIfEmpty 同为未启用库上仅有的三个可用成员,因此不经 runEnabled——经了就成了「只有启用的库才能查自己启没启用」。


listCommits()​

listCommits(options?): Promise<CommitLogPage>;

Defined in: packages/rxdb-plugin-working-tree/src/working-tree/working-tree-facade.ts:341

当前分支从 HEAD 沿完整父链可达的提交历史(FR-012)。

Parameters​

ParameterTypeDescription
optionsCommitLogOptions条数上限、时间窗与实体过滤;全部可选

Returns​

Promise<CommitLogPage>

见 CommitLogPage;一次都没提交过的库是 entries 为空的一页

Throws​

WorkingTreeCapabilityDisabledError 这个库还没启用提交能力

Throws​

RxDBError 当前分支的 ref 行缺失时(0004 没跑完,不是空历史)

Remarks​

与 status / diff 同样不收 branchId:读的恒为当前 active 分支 (FR-048)。别的分支的历史拿回去,既不能在它上面提交也不能丢弃,而「当前在哪条分支」 由 active 分支唯一确定——开一个入参等于允许调用方问一个它无法作用于的对象。

历史 ≠ rxdb_commit 全表:CAS 丢掉的提交与被删分支留下的节点,行都还在,但没有任何 ref 指向它们。可达性遍历在 commit/list-commits.ts,翻译成公开条目在 commit/commit-log.ts。


restore()​

restore(target, options): Promise<WorkingTreeRestoreResult>;

Defined in: packages/rxdb-plugin-working-tree/src/working-tree/working-tree-facade.ts:386

把一个可达历史 commit 的内容作为新的未提交变更写回当前工作树(FR-013)。

Parameters​

ParameterTypeDescription
targetWorkingTreeRestoreTarget见 WorkingTreeRestoreTarget;entities 缺省即整个 commit
optionsWorkingTreeCredentials见 WorkingTreeRestoreOptions;三个捕获位必填

Returns​

Promise<WorkingTreeRestoreResult>

见 WorkingTreeRestoreResult;不可达 / 脏工作树 / 不兼容都是 ok: false 的返回值

Throws​

WorkingTreeCapabilityDisabledError 这个库还没启用提交能力

Throws​

CommitGraphCorruptedError 当前分支的提交图已损坏(FR-051)

Remarks​

恰好两个必填位置参数,与 commit 同形。options 做成可选的话,「缺省时由本次调用 内部读 revision」就成了合法用法,而内部读到的恒等于当前值、CAS 永远命中——FR-034 对 restore 的那半句当场失效。

没有 checkout(),也没有游离 HEAD(硬裁决 5):这个成员是「把历史内容搬进工作树」, 不是「把 HEAD 挪过去」。恢复完的工作树是脏的,下一步是 commit() 或 discard(),与手写变更 走同一条路(FR-015)。


restoreSession()​

restoreSession(): Promise<
| WorkingTreeRestoreSessionInfo
| null>;

Defined in: packages/rxdb-plugin-working-tree/src/working-tree/working-tree-facade.ts:408

当前分支那个尚未结束的恢复会话(contracts/core-api.md §5)。

Returns​

Promise< | WorkingTreeRestoreSessionInfo | null>

见 WorkingTreeRestoreSessionInfo;没有未结束会话时为 null

Throws​

WorkingTreeCapabilityDisabledError 这个库还没启用提交能力

Remarks​

与 status / listCommits 一样不收 branchId:会话恒属当前 active 分支 (FR-048)。别的分支上的恢复会话拿回来,既不能在它上面提交也不能丢弃。

「还成不成立」不在这里回答:那是 status() 的 restoring / conflicted 两位,判定只有那一份。 这个成员只回答「有没有、来自哪个 commit」。


runEnabled()​

protected runEnabled<T>(run): Promise<T>;

Defined in: packages/rxdb-plugin-working-tree/src/working-tree/working-tree-facade.ts:461

受管成员的唯一入口:开写事务、过能力门禁、再跑命令体。

Type Parameters​

Type Parameter
T

Parameters​

ParameterTypeDescription
run(executor, adapter) => Promise<T>命令体;拿到的执行器与门禁读能力行用的是同一个,第二参是本纪元的本地适配器

Returns​

Promise<T>

命令体的返回值

Throws​

WorkingTreeCapabilityDisabledError 未启用时

Throws​

RxDBError 能力行缺失时(0004 迁移没写入那一行)

Throws​

UnsupportedRxDBSystemVersionError 能力版本三元组与本进程不符时

Remarks​

四步顺序都是有理由的:

  1. 先开事务。门禁读到的启用态与命令体的写入必须同进同出,否则两者之间隔着 一个别人可以 disable 的窗口(v1 还没有 disable,但这个窗口不该靠「暂时没人 能用它」来关闭)。
  2. 读能力行,行缺失时让 readCommitCapability() 自己抛。缺行是「表建了但没有 那一行」的损坏现场,不是关闭状态;按关闭处理等于让用户以为点一下 enable() 就好了,而那条 CAS 会打在一张空表上、命中 0 行。
  3. 未启用先于版本比对。一个还没启用的库,最该听到的是「去 enable()」, 而不是「你的 codec 版本是 1、本进程要 2」——后者对它毫无可操作性。
  4. 版本比对复用 assertSupportedCommitCapability(),不在这里另写一遍比较。 两份比较逻辑迟早会在某次 bump 时分岔,而 fail-closed 恰恰依赖它们一致。
  5. 提交之后补一次捕获自愈(#healCapture(),理由见它自己的 @remarks)。 它排在事务外面,与 enable() 里那一句同理由:装在事务里的话, 捕获会开始拦截这同一笔事务余下的写。
  6. 回滚之后补一次损坏闩(latchBranchCorruption())。commit() / restore() / discard() 三条路径都在这笔事务里跑 assertCommitGraphIntact(),而命中损坏的那一笔 注定回滚——标记写在里面等于写完就没。于是三条路径各自去 catch 一次?那三份 catch 会在下一条受管成员加进来时漏掉第四份。放在唯一入口上,新成员天然带着这条闩。

那个 catch 只补一件事就把原错原样抛回去:它不认识的错误一个字都不改(latchBranchCorruption 自己判类型),也绝不让落标记这一步的失败顶替掉手上那个真正的错误。

不再经 #runInTransaction(),虽然前三行与它逐字相同:自愈要拿到这一笔事务用的那个 适配器,而那个方法只交出命令体的返回值。再走一次 localAdapter$ 可能取到另一个纪元的 实例——给那一个装钩子,本纪元照旧不捕获。

受管成员的参数校验必须写在 run 里面:写在调用 runEnabled() 之前的话, 未启用的库会先回答「message 不能为空」——一个在这个库上根本无从谈起的问题。

适配器作为第二参交下去,而不是让命令体自己再解析一次。 需要它的是提交写路径的 at-rest 判定槽位(createCommitWriteContext(),FR-038);命令体自己走 rxdb.localAdapterSync 会在未连接的库上直接抛,而再走一次 localAdapter$ 则可能取到 另一个纪元的实例——那个实例的判定器与本事务写的是两个库。只多写一个形参: 少写形参的回调在 TS 里仍然可赋值,不需要它的成员一个字都不用改。


status()​

status(): Promise<WorkingTreeStatus>;

Defined in: packages/rxdb-plugin-working-tree/src/working-tree/working-tree-facade.ts:259

当前分支的工作树摘要(FR-004)。

Returns​

Promise<WorkingTreeStatus>

见 WorkingTreeStatus

Throws​

WorkingTreeCapabilityDisabledError 这个库还没启用提交能力

Remarks​

零参:摘要问的是「当前分支现在怎么样」,而「当前分支」由 active 分支唯一确定 (FR-048)。开一个 branchId 入参等于允许调用方问别的分支,而那条分支的 workingTreeRevision 拿回去既不能提交也不能丢弃——三个捕获位只对 active 分支有效。