English | 中文
零依赖的原子文件替换,供绝不允许在磁盘上留下不完整、被符号链接劫持或权限过宽内容的文件型存储共用:用户设置文档(dsh-settings-file)与凭据存储(dsh-credentials-local)。
import { withFileLock, writeFileAtomic } from '@deepseek-ai/dsh-atomic-write'
declare const text: string
declare const render: (previous: string) => string
await writeFileAtomic('/home/u/.dsh/settings.yaml', text, { mode: 0o600 })
// Read-modify-write against the same file from several processes.
await withFileLock('/home/u/.dsh/settings.yaml', async () => {
await writeFileAtomic('/home/u/.dsh/settings.yaml', render(text), { mode: 0o600 })
})
writeFileAtomic 提交一份已经渲染好的字符串。约定按故障利用它的先后顺序列出:
wx + 随机后缀):open 拒绝跟随预先埋在可猜测临时路径上的符号链接。mode 走完 rename:替换权限过宽的旧文件时直接收窄,不存在 chmod 竞态。mode 为必填,让权限决策始终可见于每个调用点(与所有新建 inode 一样受进程 umask 影响)。rename 替换的是符号链接目标本身,绝不写穿到其指向的文件。withFileLock 跨进程串行化同一文件的写入方,服务于单靠原子提交无法保证安全的读-渲染-提交循环。锁是以 wx 创建的同目录 <filename>.lock,因此读取方从不参与竞争;等待方按指数退避,超时即失败而非无限阻塞。EEXIST 直接表示竞争;只有一次新的 lstat 确认锁路径存在时,EPERM 才表示竞争,从而兼容 Windows 的独占创建行为,又不掩盖无关的权限故障。竞争者绝不移除现有锁:锁龄无法区分已经崩溃的所有者与被暂停但仍存活的写入方。
等待多久是持锁方所跑操作的属性,因此由每次调用经 waitMs 声明。默认值只按纯文件工作量级选定;若持锁方的循环包含一次网络往返——例如刷新过期 token 的凭据变更——就应声明更长的值,否则该文件的其他写入方会在这段时间内全部失败。退避节奏保持固定:它决定竞争者多久问一次,调用方没有理由改变它。
无:本包是纯文件系统原语,此处没有任何内容会到达模型请求。
无;此处没有任何内容会进入请求前缀。
fsync,因此崩溃后可能观察到 rename 被回退。此处的文件型存储在启动时重新读取并重新发布,把持久性留作调用方的策略。Buffer 或流式形态。