跳到主要内容

v0 → 1.0 升级

Aiao 目前处于 0.x 阶段(当前发布版本 0.0.25)。按 semver,0.x 期间次版本即可包含破坏性变更。本页说明 1.0 将稳定哪些内容,以及破坏性变更如何被追踪和公布。

版本策略、公开 API 范围与废弃周期见版本与 API 稳定性策略。

1.0 将稳定的范围​

1.0 发布后,以下对外表面进入 semver 维护,破坏性变更只能随主版本发布:

  • 核心:@aiao/rxdb 的实体装饰器、Repository/查询接口、RxDB 生命周期 API
  • 适配器契约:@aiao/rxdb-adapter-* 的公开构造与选项类型
  • 框架绑定:@aiao/rxdb-{angular,react,vue} 的 hooks / provider 公开 API(三端对称)
  • 插件契约:@aiao/rxdb-plugin-* 的注册入口与选项类型

不在稳定范围内的内容(内部实现、@internal 标注符号、未文档化导出)见稳定性策略。

破坏性变更如何追踪​

  • 每个公开包维护 API 基线快照(requirements/api-baseline/<pkg>.json),记录导出符号表面。
  • CI 在每次变更时对比基线:未声明的破坏性变更会导致检查失败。
  • 确属破坏性的变更必须:更新基线、在 PR 中标注 breaking、并在本页补充升级说明。

0.x 期间的升级建议​

在 1.0 之前:

  1. 锁定次版本:升级前查阅 CHANGELOG。
  2. 同步升级 @aiao/*:所有包共用同一发布版本号,避免混用不同版本。
  3. 对照兼容矩阵:确认 Node / 框架 / 适配器版本,见兼容矩阵。
  4. 跑通类型检查:本项目对公开类型做编译期契约测试;升级后先 tsc 验证消费端。

升级步骤(通用)​

# 1. 同步升级所有 @aiao/* 到目标版本
pnpm up "@aiao/*@latest"

# 2. 重新安装并构建
pnpm install
pnpm build

# 3. 运行类型检查与测试,确认无破坏
pnpm tsc --noEmit

若升级涉及数据结构变化,配合 Schema 迁移 编写一次性迁移脚本。

版本专属破坏性变更​

1.0 正式发布时,本节将逐版本列出破坏性变更与对应迁移步骤。当前 0.x 阶段的变更记录见 CHANGELOG。

0.0.25 之后(未发布)​

第 13 项与第 68 项改的是接口成员或函数参数。requirements/api-baseline/*.json 只按导出符号的名字和种类 (value / type / both)逐个比对,看不见这一类变化,所以在此手工登记。第 4、5 项是普通的导出移除, 基线已随提交更新(rxdb-plugin-querycache.json / rxdb.json),一并列在这里方便查阅。

第 1 项与第 58 项影响从 0.0.25 升级的应用。第 24 项动的是 0.0.25 之后才出现、还没有发布过的 API: 只从 0.0.25 升级的应用碰不到它们;跟着 main 开发、用过这些 API 的代码需要照下面改。

1. SwitchBranchOptions 新增必填成员 prepare​

prepare 是切换事务内的前置钩子:适配器解析出目标分支之后、动第一行之前 await 它,抛出即回滚 整次切换。

直接调用 adapter.switchBranch() 的代码必须传入 prepare,否则编译期报错。不需要前置校验的 调用点显式传入哨兵实现:

import { SKIP_BRANCH_SWITCH_PREPARE } from '@aiao/rxdb';

// before
adapter.switchBranch({ branchId, actions });

// after
adapter.switchBranch({ branchId, actions, prepare: SKIP_BRANCH_SWITCH_PREPARE });

自行实现适配器 switchBranch() 的,要在切换事务内、解析出目标分支之后、改 activated / 重建触发器 / 套用 actions 之前,恰好调用一次 options.prepare({ executor, targetBranchId }); 它抛出的错误原样上抛,不要 catch。

2. uninstallWorkingTreeCapture 去掉了 raw 参数​

// before
function uninstallWorkingTreeCapture(target: WorkingTreeCaptureMountTarget, raw: RawWritePrimitives): void;

// after
function uninstallWorkingTreeCapture(target: WorkingTreeCaptureMountTarget): void;

调用方删掉第二个实参即可;卸载不再需要调用方回传原始写方法。

3. RxDBSystemContribution:assertBranchSwitchable 更名为 prepareBranchSwitch,另增两个必填方法​

  • assertBranchSwitchable → prepareBranchSwitch。签名不变((context: RxDBBranchSwitchContext) => Promise<void>), 语义变了:以前在调用方另开的只读事务里、切换之前执行;现在由适配器经 SwitchBranchOptions.prepare 在切换事务内部回调,context.executor 可写,写下的行与这次切换同生共死,抛出即回滚整次切换。 该落的只有切换本身必然带来的行(例如激活代际 +1),业务数据不在此列。
  • 新增两个必填方法:
    • takeOverBranchSwitch(context: RxDBBranchSwitchTakeoverContext): Promise<RxDBBranchSwitchTakeover>: 不接管的贡献方返回 'not_applicable',主流程照常走普通切换
    • settleBranchSwitchFailure(context: RxDBBranchSwitchFailureContext): Promise<void>: 没有诊断要落的贡献方返回一个已决 promise

贡献点(不计 capability / version / packageSpecifier 三个身份字段)从 7 个变为 9 个,全部必填。 自行实现 RxDBSystemContribution 的插件要完成更名并补齐两个新方法,结构类型检查会在编译期直接指出 缺失的成员。

4. @aiao/rxdb-plugin-querycache 收窄公开导出(移除 10 个根导出)​

以下符号不再从包根导出:assertQueryCacheCapabilities、createQueryCachePrimary、 DEFAULT_QUERY_CACHE_SYNC_STALE_TIME、queryCacheFingerprint、QueryCacheFingerprintInput、 QueryCachePrimaryLocalAdapter、QueryCachePrimaryRepository、QueryCacheSyncMemo、 QueryCacheSyncResult、RxDBQueryCacheEngineFactory。它们是读路径的内部装配件, rxdb.use(rxDBPluginQueryCache) 之后由插件自己装配,仓库内没有任何包外引用。

包根只保留 rxDBPluginQueryCache、RxDBPluginQueryCache、RxDBPluginQueryCacheOptions 与 QueryCacheEngine(实验档)。直接导入过被移除符号的代码,改为注册插件后经 rxdb 使用读路径。 读路径横跨 @aiao/rxdb-plugin-history、@aiao/rxdb-plugin-sync、@aiao/rxdb-plugin-querycache 三个包,只注册 querycache 一个会在 connect() 抛 RxDBMissingPluginError:

rxdb.use(rxDBPluginHistory);
rxdb.use(rxDBPluginSync);
rxdb.use(rxDBPluginQueryCache);

详见 QueryCache 读引擎拆包。

5. @aiao/rxdb 根入口不再导出九个实体内部函数​

entity.utils 由 export * 改为具名导出,以下九个函数不再从包根导出:

  • setSafeObjectKey、setRuntimeObjectKey、setRuntimeObjectGetter、setSafeObjectWritableKey、 setSafeObjectKeyLazyInitOnce:往实体类与实例上挂不可枚举元数据的 Object.defineProperty 包装
  • fillDefaultValue、fillInitValue:实体构造期的填充步骤
  • getNeedSaveEntities、getNeedRemoveEntities:保存/删除路径上递归收集关联实体的步骤

它们都没有公开替代。getEntityStatus(entity) 上仍有同名的 getNeedSaveEntities() / getNeedRemoveEntities() 方法,但只看这一个实体的直接关系,不做被移除函数那样的递归收集与过滤, 不能原样替换。

6. 触发器 / 建表 SQL 生成函数不再默认写 main,branchId 改为必填​

触发器把 branchId 烙成 SQL 字面量,决定这张表之后每一次写入记到哪条分支名下。以前生成函数在 调用方没给时顶上 'main',库停在别的分支时写错了也不报错,只有事后审计历史才看得出来。现在每个 调用点都要自己给出分支 id,新库建表就显式传 MAIN_BRANCH_ID:

  • @aiao/rxdb-adapter-sqlite-core 的 generate_table_trigger_sql(metadata, options):options.branchId 由可选改为必填,运行时缺失即抛 RxDBAdapterSqliteError。
  • @aiao/rxdb-adapter-pglite 的 generate_trigger_sql(metadata, options):同上,缺失即抛 RxdbAdapterPGliteError。
  • @aiao/rxdb-adapter-sqlite-core 的 create_tables_sql:新增必填的第三个位置参数 branchId, 原来的第三个参数 entities 顺延为第四个。
import { MAIN_BRANCH_ID } from '@aiao/rxdb';

// before
generate_table_trigger_sql(metadata, { resolveEntityMetadata });
create_tables_sql(adapter, EntityTypes, entities);

// after
generate_table_trigger_sql(metadata, { branchId: MAIN_BRANCH_ID, resolveEntityMetadata });
create_tables_sql(adapter, EntityTypes, MAIN_BRANCH_ID, entities);

库可能已经停在非 main 分支上时(例如给既有库补建实体表),要先在同一个事务里读出当前活动分支再传进来; RxDBAdapterSqliteBase.createTables 就是这么做的。pglite 的 create_tables_sql / create_tables_statements 签名不变:它们建表期仍按 main 生成,由 RxDBAdapterPGlite.createTables 在同一事务里按当前分支重挂。

7. getEntityMutations 的入参字段改为 camelCase​

@aiao/rxdb 的 getEntityMutations(options) 两个入参字段改名,语义不变:

// before
getEntityMutations({ need_save_entities, need_remove_entities });

// after
getEntityMutations({ needSaveEntities, needRemoveEntities });

TS 调用方会在编译期报错。JS 调用方(以及绕过类型检查的调用点)得不到任何提示:旧键解构出 undefined,运行到遍历那一步抛 TypeError。入参类型现在以 EntityMutationsOptions 导出,需要单独 声明入参的代码直接引用它。

8. RxDB 接口不再声明 RxDBSync / RxDBMigration / RxDBChange / RxDBBranch 四个成员​

@aiao/rxdb 以前用 declare module '@aiao/rxdb' 给 RxDB 接口增广了这四个成员(类型是同名系统实体类), 但运行时从来没有给它们赋过值:rxdb.RxDBChange 编译得过,读出来却是 undefined。这段增广已删除, 这样写的代码现在会在编译期报错。四个实体类一直从包根导出,改为直接导入:

// before
rxdb.RxDBChange; // 运行时是 undefined

// after
import { RxDBChange } from '@aiao/rxdb';

参考​