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
| Parameter | Type | Description |
|---|---|---|
rxdb | RxDB | 宿主实例;整个门面只持有它这一个引用 |
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
| Parameter | Type | Description |
|---|---|---|
message | string | 提交消息 |
options | CommitOptions | 见 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
| Parameter | Type | Description |
|---|---|---|
commitId | string | 要读的 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
| Parameter | Type | Description |
|---|---|---|
options | WorkingTreeDiffOptions | 粒度、实体过滤与分页;全部可选 |
Returns
Promise<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
| Parameter | Type | Description |
|---|---|---|
options | WorkingTreeCredentials | 见 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
| Parameter | Type | Description |
|---|---|---|
options | CommitLogOptions | 条数上限、时间窗与实体过滤;全部可选 |
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
| Parameter | Type | Description |
|---|---|---|
target | WorkingTreeRestoreTarget | 见 WorkingTreeRestoreTarget;entities 缺省即整个 commit |
options | WorkingTreeCredentials | 见 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
| Parameter | Type | Description |
|---|---|---|
run | (executor, adapter) => Promise<T> | 命令体;拿到的执行器与门禁读能力行用的是同一个,第二参是本纪元的本地适配器 |
Returns
Promise<T>
命令体的返回值
Throws
WorkingTreeCapabilityDisabledError 未启用时
Throws
RxDBError 能力行缺失时(0004 迁移没写入那一行)
Throws
UnsupportedRxDBSystemVersionError 能力版本三元组与本进程不符时
Remarks
四步顺序都是有理由的:
- 先开事务。门禁读到的启用态与命令体的写入必须同进同出,否则两者之间隔着
一个别人可以
disable的窗口(v1 还没有 disable,但这个窗口不该靠「暂时没人 能用它」来关闭)。 - 读能力行,行缺失时让
readCommitCapability()自己抛。缺行是「表建了但没有 那一行」的损坏现场,不是关闭状态;按关闭处理等于让用户以为点一下enable()就好了,而那条 CAS 会打在一张空表上、命中 0 行。 - 未启用先于版本比对。一个还没启用的库,最该听到的是「去 enable()」, 而不是「你的 codec 版本是 1、本进程要 2」——后者对它毫无可操作性。
- 版本比对复用
assertSupportedCommitCapability(),不在这里另写一遍比较。 两份比较逻辑迟早会在某次 bump 时分岔,而 fail-closed 恰恰依赖它们一致。 - 提交之后补一次捕获自愈(
#healCapture(),理由见它自己的 @remarks)。 它排在事务外面,与enable()里那一句同理由:装在事务里的话, 捕获会开始拦截这同一笔事务余下的写。 - 回滚之后补一次损坏闩(
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>
Throws
WorkingTreeCapabilityDisabledError 这个库还没启用提交能力
Remarks
零参:摘要问的是「当前分支现在怎么样」,而「当前分支」由 active 分支唯一确定
(FR-048)。开一个 branchId 入参等于允许调用方问别的分支,而那条分支的
workingTreeRevision 拿回去既不能提交也不能丢弃——三个捕获位只对 active 分支有效。