用途:attach-primitive 是 epicenterhq/epicenter 仓库中 packages/workspace(以及 packages/cli 中会话型原语)的契约与不变量文档,用于统一所有持久化、同步、物化与绑定类构建块的写法。
主要能力:区分 attach* 与 create*。attach* 有副作用,把观察者、销毁监听或订阅状态注册到主体参数上,返回调用时即固定表面的普通对象;create* 是纯构造或模块单例引导,不接收主体参数。契约适用于接收主体(通常是 Y.Doc、某个 attachment 或类似可组合目标)并对其进行装饰的函数,不适用于应用代码中的模块级工厂单例和自持生命周期的顶层编排器。
使用方式:常见形式为 attachX(subject, opts),第一个参数是被修改的主体,几乎总是 Y.Doc,例如 attachIndexedDb(ydoc)、attachSqlite(ydoc, { filePath })、openCollaboration(ydoc, { url, openWebSocket, replicaId, actions })、attachBroadcastChannel(ydoc)、attachAwareness(ydoc, defs)、attachRichText(ydoc) 等。表与 KV 存储不再逐个附加,而由 createWorkspace({ id, tables, kv }) 作为捆绑构造,暴露 workspace.ydoc、workspace.tables、workspace.kv;把 workspace.ydoc 传给其余 attach* 原语,把整个 workspace 捆绑传给物化器。物化器以 workspace 捆绑为主体:SQLite 镜像 workspace.tables 中所有表,fts 为可选按表配置;Markdown 只镜像面向人的子集,必须通过 perTable 选择,存在即选中、值即配置,未出现的表跳过。
不变量:构造同步返回,启动工作放入返回对象上的语义化 when* promise;销毁挂接到主体生命周期(Y.Doc 用 ydoc.once('destroy', ...),不在 attachment 上暴露 .destroy());清理需幂等;返回普通数据,不用 ES 类或惰性初始化 getter;绑定 Y.Doc 的原语不接收 id 选项,使用 ydoc.guid;屏障命名按语义(whenLoaded、whenConnected、whenChecked、whenFlushed,whenReady 仅用于捆绑聚合)。需要门控启动的原语接受 waitFor?: Promise<unknown>,在首次动作前等待。
适用场景与限制:适用于在 workspace 构建闭包内组合原语、聚合子屏障为 whenReady、浏览器捆绑提供 wipe()、守护进程捆绑用 Symbol.asyncDispose。反模式包括复活 ExtensionContext、用 createWorkspace().with(...) 链、在 Y.Doc 绑定 attachment 上暴露 dispose()、鸭子类型判断 attachment、在 Y.Doc 绑定原语上取 id、对接收主体的副作用原语使用 createX、在裸 Y.Doc 上逐个附加表或 KV。参考实现位于 packages/workspace/src/document/attach-indexed-db.ts、open-collaboration.ts、create-workspace.ts 及 materializer/markdown/materializer.ts。非 Y.Doc 主体模式目前仓库中暂无示例。