npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@xfcodeai/dsh-settings

v0.1.5-rc.5

Published

Abstract user-settings seam (ctx.settings) for the DeepSeek Harness

Readme


description: "面向插件作者与维护者的用户设置服务:注册可配置 namespace、读取解析值或接入配置界面。" kind: "package-reference"

@xfcodeai/dsh-settings

English | 中文

概述

当用户需要在运行时修改插件配置,而无需重启或重新读取 cordis.yml 时,请使用本包。每个 namespace 合并 schema 默认值、部署配置与用户覆盖;读取方会得到深冻结的解析值快照,并可观察已提交的变更。写入只影响用户覆盖、按 namespace 串行执行,并可拒绝陈旧 revision,避免覆盖较新的变更。持久化运行时编辑需要先配置设置存储;否则插件仍可继续使用组合配置。

目录


使用本包

插件与配置界面通过 ctx.settings 在运行时读取并修改配置。常用路径:挂载提供方、用 schema 注册 namespace、读取并观察解析值,并通过 owner scope 写入。

何时选择

当插件的配置需要在运行时可变——用户编辑文档或配置界面修改——且无需重启或重读 cordis.yml 时,选择设置服务。它适合多个插件各拥有一个配置 namespace、以及配置界面需要渲染 schema、标记用户覆盖字段并持久化编辑的场景。当配置在加载时固定则没有必要:没有挂载提供方时一切照旧,配置保持组合原样。

挂载提供方

服务本身不存储任何内容;请挂载一个提供方,例如随附的文件型提供方:

- name: '@xfcodeai/dsh-settings-file'
  config:
    path: /absolute/path/to/settings.yaml

提供方上线后 ctx.settings 即出现。完整配置面由提供方 README 负责;生成的配置目录列出每个受支持字段。

注册 namespace

插件用 schemastery schema 注册自己的 namespace,并可选地把组合配置作为 base 层传入,让解析值从部署已配置的内容起步:

const scope = ctx.settings.register('ui-theme', ThemeSchema, {
  base: config,   // composition entry config; the user layer resolves above it
})
const theme = scope.get()              // deep-frozen resolved snapshot
scope.update({ density: 'compact' })   // merges into the user section and persists

TypeScript 会按小写字母、数字与连字符文法检查字面量 namespace 参数;运行时动态传入的字符串接受相同校验。ctx.settings.installSection(owner, ns, schema, entry, hooks) 为消费方插件封装可选服务接线:只要设置服务存在,它就用插件的组合配置作为 base 注册 namespace;服务消失时插件回退到组合配置,行为与原先完全一致。

读取与观察值

get(ns) 以深冻结快照返回解析值,namespace 未注册时为 undefined。watch(callback) 在每次已提交变更后以 (next, prev) 调用回调:同一回调的调用按提交顺序逐个执行,异常被隔离并记入日志,因此慢或抛错的观察者绝不会阻塞或破坏其他观察者。

写入值

update(ns, patch) 把普通对象 patch 深合并进用户分节——绝不进 base——校验解析候选值、经提供方持久化后提交。replace(ns, section) 整体替换用户分节,是删除/重置路径:replace({}) 重新继承 base 与 schema 默认值。mutate(ns, ops) 在写入排到队首那一刻的分节上按序施加 { op: 'set' | 'unset', path } 编辑——这是持有不完整(例如脱敏后)视图的调用方的删除路径,因为按协议接口返回的内容重建分节再整体替换,会删掉协议从未回传的每个字段。

每次写入都会拒绝与 JSON 不兼容的数据(Date、Map、BigInt、非有限数或循环引用会在任何内容持久化前以 $ 为根的路径报错)、拒绝只读提供方上的写入,并可接受可选的 expectedRevision:把 descriptor 中的 revision 传回,namespace 已越过该值时写入会被 SettingsConflictError 拒绝,而不是覆盖先完成写入的一方。

配置界面

describe() 为每个已注册 namespace 返回一条 descriptor:序列化 schema、解析值、分离的 base 与 user 层(字段出现在 user 中即标记为用户覆盖)、生效时机与 namespace 的 revision。每个协议接口都必须传入 redactSecrets: true:它从每一层剥离 role('secret') 字段,并把它们枚举为 { path, set } slot,让页面可以渲染只写输入而不接触任何机密。documentPath 与 prepareDocument() 在提供方拥有用户可编辑文件时把它暴露给原生编辑器。

事件与失败

settings/updated (ns, next, prev, source) 在每次已提交变更后触发——进程内写入(source: 'update')或外部观察到的编辑(source: 'provider')——解析值深相等时绝不触发。settings/document-updated (ns, revision) 在原始用户分节发生变化时触发,即使解析值没有变——已打开的编辑器正需要它来得知字段从继承变为覆盖。schema 拒绝的存量分节在重载时保留该 namespace 的最后可用值并告警;注册时同样的失败会直接拒绝注册。


理解实现

本节解释服务背后的设计决策并指出实现它们的代码位置;可观察行为已在使用本包中完整说明。

设计理念

  • 分层解析,单一用户层。 namespace 的值依次为 schema 默认值、注册方的组合 base、用户文档分节;写入只触碰用户层,因此 replace({}) 是真正的重置。
  • 提交以深相等为门槛。 只有解析值变化时 settings/updated 才触发;原始分节事件独立存在,因为配置界面还必须得知「继承变成了覆盖」。
  • 写入排队并做 revision 检查。 每个 namespace 的写队列按调用顺序串行,expectedRevision 在队首判断——那里服务才能分辨持有新鲜快照的写入方与持有陈旧快照的写入方。
  • 观察者与监听器异常被隔离。 watcher 调用与事件扇出隔离同步抛出与异步拒绝,一个坏掉的观察者不会卡死提交或提供方的重载循环;INVARIANT 编码的失败在所有监听器执行完后重新抛出。
  • 注册是 fiber 上的 effect。 注册 namespace 是调用方插件 fiber 上的 effect:dispose(资源释放)该 fiber 即移除 namespace 及其观察者。

源码地图

| 文件 | 职责 | |---|---| | src/index.ts | Service Definition:namespace 校验、注册、解析、写队列、describe/脱敏、事件、installSection | | src/redact.ts | redactSecrets 遍历器:剥离 role('secret') 字段并枚举其 slot | | src/types.ts | 客户端安全类型面:事件声明、SettingsNamespace、SettingsUpdateSource | | src/invariant.ts | 不变式伴生插件:settings/updated 只对已注册 namespace、只在解析值变化时、且携带权威值触发 |

解析与写入路径

每次写入都在调用时对输入做快照(分离并校验 JSON 形状的数据),然后排上该 namespace 的串行链。在队首,服务按当前状态重读分节、检查 expectedRevision、合并/替换/编辑、经 schema 与 owner 的可选 validate 解析并校验候选值、经提供方持久化,然后才提交并发出事件。registrant fiber 在写入途中被 dispose 的写入仍到达存储,但不会提交、也不会通知任何人;卸载先拒绝新写入,并排干排队写入与已启动的 watcher 调用后才完成。

变更检测与事件

commit 用 seam 的 deepEqualJson 谓词比较解析值,并逐监听器扇出 settings/updated。bumpRevision 比较原始分节并携带新 revision 发出 settings/document-updated;它与解析值检查相互独立。两个扇出以相同方式隔离监听器异常。

客户端安全类型

./types 子路径出口持有事件声明及其签名点名的 SettingsNamespace、SettingsUpdateSource 类型,包根继续 re-export 这些类型。于是 Host 编译面之外的消费方读到的正是 Host 发射的那一份签名,而不必再写一遍。


进一步探索

当服务级约定不够用时阅读以下页面。它们从共享子系统词汇逐步进入随附提供方与能力架构。

  • 设置子系统参考——namespace、注册、owner scope、descriptor、变更提交与生成的 cordis 接口面。
  • 文件型设置提供方——随附的 YAML/JSON 提供方:配置、热重载、保留注释的写入。
  • 设置包映射——用户设置能力的两个包及其角色。
  • 能力 seam——本服务遵循的 Service Definition / Service Provider / Consumer 拆分。

模型体验

间接生效:由设置值提供的所有面向模型的内容均由消费方插件负责;本服务只存储并解析用户设置,自身不注册任何面向模型的内容。

KV Cache 影响

无直接失效;把设置值纳入请求前缀的消费方负责该变更。

已知限制与延期工作

这些限制说明本服务何时不合适或需要特别注意。它们是当前包约束,不是任务积压。

  • 单一用户层——解析只认识 schema 默认值、一个组合 base 与一个用户文档;它不记录每个解析值由哪一层提供。
  • redactSecrets 并非一条可被证明的协议边界——遍历器只跟随 object/dict/array 容器,因此只能经由 union、intersection 或 transform 抵达的 role('secret') 字段会被原样返回,且 secrets 列表为空;序列化 schema 还会把 secret 字段的默认值带给每个客户端。两种情况都不会被拒绝;机密无法经由被遍历的容器抵达的 schema,绝不可注册到暴露于协议的 namespace 上。fail-closed 的 describeForWire()——拒绝自己无法证明安全的 schema,并对序列化封装与错误文本做净化——是暂缓的答案。
  • 跨进程并发由提供方定义——服务仅在进程内按 namespace 串行写入;跨进程并发按提供方行为收敛(文件提供方在写锁下读-改-写,因此并发写入者不会丢掉彼此的 namespace,同 namespace 冲突按后写胜出解决)。

开发备注

本开发备注是维护者的工作上下文:尚未决定的开放设计方向。它明确非权威——已发布的行为、限制与已接受的理由见上文各节与包代码。代码 TODO 中记录的开放方向:把公开的 ns 参数更名为 namespace(API、提供方约定、实现、测试与消费方同步);释放注册项时停用所有 watcher 并等待其调用链完成,确保回调不会在 registrant fiber 释放后继续运行;替换注册从持久化分节重新解析,让进行中的旧写入不会把它留成陈旧值;改用属性安全的对象构造,让 __proto__ 这类合法 JSON 键保持为自有数据。fail-closed 的 describeForWire() 净化器是上文脱敏限制的暂缓答案。