插件开发范式

插件状态

在每个 Plugin node 的原生 TinyBase Store 中保存偏好与有界业务记录。

用户偏好、开关和小型结构化业务记录使用 Host 托管的 TinyBase。部署配置继续用 configs.use(),凭据用 Vault,文件和二进制用 FS。大量记录、复杂 SQL 或多进程事务使用 SQL 数据库,可选择 Services 托管或应用完全自管。

每个 Core 与 Host 都内建 FS 和 TinyBase;插件直接使用固定的 ctx.tinybase,无需安装服务。后端选择决定是否跨 Host 保留数据。插件不选择存储目录、driver 或持久策略。

打开并使用原生 Store

以下是完整的 Plugin 示例。初始化失败沿现有 init 失败路径传播;Store 连接和关闭排空由 Host 管理:

// @filename: preferences.ts
import { ,  } from '@pluxel/core'
import { , type Store } from 'tinybase'
import * as  from 'valibot'

const  = .({ : .(['system', 'light', 'dark']) })

@()
export class  extends  {
	private readonly  = this..
	private !: Store

	protected override async (): <void> {
		this. = await this..(().({ : 'system' }))
		.(, this..())
	}

	() {
		return .(, this..()).
	}

	async (: unknown): <void> {
		const  = .(, )
		this..('theme', .)
		await this..()
	}
}

open() 不传参数时由 Core 创建普通 Store;传入时返回同一 native 实例。也可在 open 前使用原生 setTablesSchema()、setValuesSchema() 或 Valibot schematizer 配置 schema。Valibot 仍从上游直接导入,不需要第二套 Pluxel 校验接口。

open 完成前不使用或公开该 Store;成功后创建原生 Queries/listeners,并在 ctx.effects 中登记相应 destroy() 或 delListener()。不要给托管 Store 添加 Persister 或 Synchronizer。原生临时 Store 不需交给 Host。

每个 Plugin node 一份 Store,Part 共用 owning Plugin 的实例;fork 的数据独立。重复或并发 open 共用恢复结果,同代换入另一 Store 明确失败。实例必须新建且无已挂载监听或未结束事务,不可跨 Host/generation 重用;不接受 MergeableStore。

初值、保存与确认

只有快照不存在时才使用初值;已有快照替换全部 Tables/Values,合法空状态也会保留。恢复会检查格式、owner、内容和原生 schema 的保真结果;损坏或 schema 丢失字段时初始化失败,源文件不被重写。业务领域校验在 open 后、公开能力前完成。

原生 mutation 同步改变内存和订阅。Host 在 Plugin 成功发布后自动保存完整 transaction,停止时排空。初始化失败不自动写回候选;init 内显式 await state.flush() 则是真实提交,即使随后 init 失败也不会撤销。

需要向调用者确认保存时,await flush() 并传播失败。它等待调用时最后一个完整事务被后端确认,可与更新快照合并;不保证每个中间状态落盘,也不回滚已经可见的内存 mutation。未 open、未结束事务、已故障或已撤回时拒绝。没有持久确认承诺的同步方法可使用普通 mutation,由 Host autosave 与关闭排空。

如果领域要求“保存前不可见”,在领域内部区分 candidate 与 applied view,并串行执行保存及发布;不能把同步 native Store 当作提交后才可见的事务。Wretch 的管理设置使用这条契约。

失败、容量与依赖

快照只接受 string、boolean 和有限 number;嵌套记录可由领域编码为 string,不能把任意对象直接作为 cell。Host 默认每份完整快照最多 8 MiB,可通过 Host 的 config.tinybase.maxSnapshotBytes 调整。超限或 IO 错误可从 flush()、启动和关闭报告观察,Host 内部诊断可查询 owner、revision 与失败状态。

快照和 writer 失败使用公开的 TinyBaseError,例如 SNAPSHOT_INVALID、BUDGET_EXCEEDED 与 SAVE_FAILED;底层失败保留在 cause。插件的 Valibot 领域校验仍抛原生校验异常,不把无效数据改成默认值。

保存失败后 writer 阻断后续保存,不在 shutdown 隐式重试。内存仍可能包含候选,后端结果也可能未知;调用方应保留失败状态,由 Host 停止并显式恢复 writer,再启动新代。FS 后端只读时无法打开托管 Store;InMemory 只能确认内存写入。停止不会删除已有数据。

Core 持有精确的 tinybase runtime dependency;应用直接使用原生 TinyBase 时声明自己的 dependency。使用共享 Store 的插件声明同版 peer 与 devDependency;原生能力始终从 tinybase 或其子路径导入。只声明 configs.use() 的插件无需再打开 Store 或添加 TinyBase 依赖。版本与打包约束包含实际版本接纳规则。

隔离验证

将上面的插件保存为 preferences.ts。测试通过真实 createTestHost() 使用默认内存后端,结束时释放 Host:

import { createTestHost } from '@pluxel/test'
import { PreferencesPlugin } from './preferences'

await using host = await createTestHost()
const preferences = await host.start(PreferencesPlugin)
await preferences.setTheme({ theme: 'dark' })

跨 Host 恢复测试使用同一临时 Passthrough 目录,关闭第一个 Host 后再创建第二个,分别检查内容、合法空状态和失败不覆盖。隔离 Host 不证明在线应用已接纳修改;在线操作使用开发控制台。

最后更新于

本页目录