跳到主要内容

WorkingTreeCaptureRuntime

Defined in: packages/rxdb-plugin-working-tree/src/working-tree/capture-hook.ts:304

捕获运行时

Remarks​

一个数据库一个实例,由 RxDB.connect()(能力位已开)或 workingTree.enable()(刚翻开) 装到本地适配器上。没启用提交能力的库上这个类一次都不会被构造。

Implements​

Constructors​

Constructor​

new WorkingTreeCaptureRuntime(options): WorkingTreeCaptureRuntime;

Defined in: packages/rxdb-plugin-working-tree/src/working-tree/capture-hook.ts:338

由 createWorkingTreeCaptureRuntime 构造;没启用提交能力的库上一次都不会被调到。

Parameters​

ParameterTypeDescription
optionsWorkingTreeCaptureRuntimeOptions全部依赖,见 WorkingTreeCaptureRuntimeOptions

Returns​

WorkingTreeCaptureRuntime

Remarks​

全部注入且构造后不可换:版本化域与系统表清单在这里定格,于是「这张表受不受保护」 在本运行时的整个生命周期里只有一个答案。构造器不碰挂载目标——#target 由 bindMountTarget 在装到适配器上的那一刻才写入,因此同一个运行时可以先造出来、 再决定装到哪个适配器上。

Accessors​

domain​

Get Signature​

get domain(): VersionedDomain;

Defined in: packages/rxdb-plugin-working-tree/src/working-tree/capture-hook.ts:323

本运行时认的那份版本化域

Remarks​

不在核心的 WorkingTreeCaptureHook 契约上:核心一次都不读它。摆上去等于让核心 替「判定认什么」的形状背书,而那正是随捕获规则变、必须留在本包的东西。

暴露在类上是给本包自己与一致性套件用的:套件要在表 / 列平面核对「哪些表受保护」, 而那份清单只有运行时手上有。交出的是清单本身不是拷贝——拷一份出来之后,插件后续登记的 派生索引列只会落进其中一份,raw 通道与捕获会对同一张表给出不同结论。

Returns​

VersionedDomain

Methods​

bindMountTarget()​

bindMountTarget(target): void;

Defined in: packages/rxdb-plugin-working-tree/src/working-tree/capture-hook.ts:354

认领安装宿主与它那份未拦截原语;installWorkingTreeCapture 之后立刻调用一次

Parameters​

ParameterTypeDescription
targetWorkingTreeCaptureMountTarget被装上挂载点的适配器实例;同时是适配器级写意图声明的作用域键

Returns​

void

Remarks​

两样东西都只有安装方知道,而两样运行时都要:target 是 declareTrustedWrite 用的作用域身份——挂载点 2 / 3 的调用方把意图声明在适配器上, 运行时得拿同一个对象才查得到;raw 则是捕获自己发写的唯一安全入口, 走被拦截的那份会立刻递归回挂载点自己。

Implementation of​

WorkingTreeCaptureHook.bindMountTarget


gateExternalNotify()​

gateExternalNotify<T>(
entityName,
namespace,
notify
): T;

Defined in: packages/rxdb-plugin-working-tree/src/working-tree/capture-hook.ts:524

转交门 2:一次 notifyExternalUpdate()(写入口语义矩阵行 11)

Type Parameters​

Type ParameterDescription
T通知体的返回类型;放行时原样透传

Parameters​

ParameterTypeDescription
entityNamestring被通知的实体名
namespacestring | undefined已知时直接用;rxdb 即系统表
notify() => T还没被调用的通知体

Returns​

T

notify() 的返回值本身

Throws​

目标是版本化业务实体时抛出;此时一条事件都不会派发

Remarks​

与 gateRawWrite 同一个理由挂在这张契约上:核心的 notifyExternalUpdate() 拦得住 自己,却回答不了「这个实体归哪一类」——那要问域,而域是捕获的。核心因此只交出实体身份, 把「归哪类、这一类在行 11 上是放是拒」整段留给运行时。

门包着通知体,不是通知之后补的一句断言:拿到的是还没被调用的回调,「先判定」于是在 类型上就是唯一写法;写成先派发再检查的话,拒绝也阻止不了事件已经发出去。

Implementation of​

WorkingTreeCaptureHook.gateExternalNotify


gateRawWrite()​

gateRawWrite<T>(sql, execute): Promise<T>;

Defined in: packages/rxdb-plugin-working-tree/src/working-tree/capture-hook.ts:512

转交门 1:一条 raw 写语句(adapter-contract.md §2 的 4 步判定)

Type Parameters​

Type ParameterDescription
T语句执行体的返回类型;放行时原样透传

Parameters​

ParameterTypeDescription
sqlstring待判定的语句原文
execute() => T | Promise<T>还没被调用的执行体

Returns​

Promise<T>

execute() 的返回值本身

Throws​

目标落在版本化业务表且不满足任何放行条件时抛出;此时 execute 一次都不会被调用

Remarks​

rawQuery?() 是适配器接口上的可选方法,核心包没法像四个挂载点那样替适配器包住它, 判定只能由各适配器自己的实现调用 gateRawWrite。而那个函数在核心里只剩一句分派: 能力位为假直接放行,为真转交到这里。判定实现一个字都不在核心——它认的是语句词法、 表名的六种物理形态与受信意图,三样都随捕获规则变。

Implementation of​

WorkingTreeCaptureHook.gateRawWrite


interceptBulkWrite()​

interceptBulkWrite(
_host,
next,
entityName,
operation
): Observable<void>;

Defined in: packages/rxdb-plugin-working-tree/src/working-tree/capture-hook.ts:460

挂载点 4:upsertMany / deleteByIds 的门禁

Parameters​

ParameterType
_hostWorkingTreeWriteHost
next() => Observable<void>
entityNamestring
operationInterceptedBulkWrite

Returns​

Observable<void>

Remarks​

同步转发,中间没有 await:门禁必须在 Observable 存在之前抛(adapter-contract.md §1.1)。

entityName 按 namespace:entity 拆一次再判:这一层的契约里没有独立的命名空间参数, 而限定名是既有写法(resolveQueryCacheTarget 就这么解析)。送进门禁的仍是原样的 entityName,拒绝信息才指得回调用方写下的那个名字。

Implementation of​

WorkingTreeCaptureHook.interceptBulkWrite


interceptMergeChanges()​

interceptMergeChanges(
host,
next,
actions,
localChanges?,
disableTriggers?
): Promise<number | void>;

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

挂载点 2:本地 mergeChanges

Parameters​

ParameterType
hostWorkingTreeWriteHost
nextMergeChangesNext
actionsSwitchVersionActions
localChanges?Omit<RxDBChange, "id">[]
disableTriggers?boolean

Returns​

Promise<number | void>

Remarks​

业务写与捕获共用 host.runInTransaction() 要来的那一个事务:mergeChanges 内部走的也是 runInTransaction,把它发到该事务 executor 的门面上,它就复用而不是新开。于是 「写了业务表却没留下单元」在这个挂载点上不存在崩溃窗口。

本次写产生的变更行要登记进 CONSUMED_CHANGE_IDS:嵌在挂载点 1 的事务体里时 (merge_branch 的 normal 策略就是这个形状),外层的增量读否则会把同一批写再判一遍。 不在嵌套形态下时格子不存在,has() 为假,连那次水位线读都不发。

Implementation of​

WorkingTreeCaptureHook.interceptMergeChanges


interceptSwitchBranch()​

interceptSwitchBranch(
host,
next,
options
): Promise<void>;

Defined in: packages/rxdb-plugin-working-tree/src/working-tree/capture-hook.ts:436

挂载点 3:switchBranch

Parameters​

ParameterType
hostWorkingTreeWriteHost
next(options) => Promise<void>
optionsSwitchBranchOptions

Returns​

Promise<void>

Remarks​

拒绝判定在 next() 之前同步完成,所以未登记的调用一行业务数据都改不了。捕获则只能在 next() 之后另开事务——switch_branch() 自己要开事务并在前后刷变更管道,包不进来。 见本文件顶部关于 §5 偏离的说明。

Implementation of​

WorkingTreeCaptureHook.interceptSwitchBranch


interceptTransaction()​

interceptTransaction(
_host,
next,
fun,
transactionLog?
): Promise<unknown>;

Defined in: packages/rxdb-plugin-working-tree/src/working-tree/capture-hook.ts:371

挂载点 1:整事务共享一个 unitId

Parameters​

ParameterType
_hostWorkingTreeWriteHost
next(fun, transactionLog?) => Promise<unknown>
funTransactionFun
transactionLog?boolean

Returns​

Promise<unknown>

Remarks​

水位线读在 fun 之前、增量读在 fun 之后,两次都用同一个 executor:换成适配器级读取 会在事务外看见一个尚未提交的区间,捕获到的行数取决于别的事务提交得多快。

没有意图声明时入口是 crud 而不是拒绝——transaction() 是所有业务写的正常通道, 对它 fail-closed 等于把整个库变成只读。受信路径的 fail-closed 落在挂载点 2 / 3 上。

带 CAPTURE_OWNED_TRANSACTION 标的事务体原样放行:那是挂载点 2 / 3 为了写工作树行 自己开的事务,业务写已经在那边按受信入口捕获过了。

Implementation of​

WorkingTreeCaptureHook.interceptTransaction


targetClassOf()​

targetClassOf(entityName, namespace?): "system" | "versioned" | "query_cache";

Defined in: packages/rxdb-plugin-working-tree/src/working-tree/capture-hook.ts:500

实体名 → 写入口语义矩阵里的目标类别

Parameters​

ParameterTypeDescription
entityNamestring实体名(不含命名空间)
namespace?string已知时按身份精确判;省略时按裸名判

Returns​

"system" | "versioned" | "query_cache"

system / query_cache / versioned 三者之一

Remarks​

先问域,域不认得才问系统表清单。 域里只有业务实体(createWorkingTreeCaptureRuntime 建域时已把系统表摘出去),所以「域认得这个名字」本身就是「它不是系统表」的证明。反过来 先判系统表就是评审 #5 那个洞:接入方把实体取名 Commit(epic-006 恰好有一张 rxdb:Commit) 之后,那张业务表的写会被整批判成 system 而静默绕过捕获——改动不进提交,且没有报错形态。

系统表清单的两种投影按手上有没有命名空间选:有就比身份(rxdb:RxDBBranch),没有才比裸名。 两者是同一份登记簿算出来的(getSystemEntityNames / getSystemEntityIdentities), 不是两份清单。不再按 namespace === 'rxdb' 一刀切:命名空间是接入方可以自己取的, 恰好叫 rxdb 的业务实体没有理由整批退出版本控制。

与 domain 同理不在核心契约上:核心的 notifyExternalUpdate() 从前要自己问一次 归类再把结果送进门禁,现在只交出实体身份、整段判定走 gateExternalNotify。 于是「归哪一类」这件事在核心侧一次都不出现。