@aalis/schema-config

中文文档
0.16.1发布于 2026/11/01MITTypeScriptES Module近 30 天下载 2547

配置表单 Schema 词汇契约包:ConfigSchema / SchemaField / SchemaGroup / SchemaArray 及 SchemaFieldTypes 扩展点;声明与读取插件配置的 defineConfig / ConfigOf / parseConfig;可按会话覆盖的字段 SessionFieldSpec / SessionFieldsOf / isSessionField,经 JSON 下发的 `WireSchemaFiel

中文使用文档
文档目录8 节
  1. 声明与读取
  2. 给人看的文字
  3. 字段类型
  4. 字段自己的校验与文字读写( validate / fromText / toText )
  5. 可按会话覆盖的字段( session )
  6. 密钥字段( secret )
  7. parseConfig 的处置
  8. 消费方式:用宽区间,别用 caret

@aalis/schema-config

配置表单 Schema 词汇契约包:ConfigSchema / SchemaField / SchemaGroup / SchemaArray 及 SchemaFieldTypes 扩展点;声明与读取插件配置的 defineConfig / ConfigOf / parseConfig;可按会话覆盖的字段 SessionFieldSpec / SessionFieldsOf / isSessionField,经 JSON 下发的 WireSchemaField / WireConfigSchema;对这套词汇的中立解释函数 defaultsFrom / deepMergeDefaults / validateConfig / readFieldValue / removeExtraFields,密钥引用的类型与工具 SecretRef / isSecretRef / mapSecretFields / SecretPosition,配置危险键闸与拷贝 isUnsafeConfigKey / cloneConfigObject,以及插件因配置问题无法激活时在 apply 里抛出的 configError / missingConfigError(后者用于缺必填项;name 为 ConfigError、不带 stack,激活失败日志只有一行,不像程序崩溃)。

@aalis/core 把插件的 configSchema 当作 opaque 数据透传、不解释任何字段;表单词汇(label / options / textarea …)属呈现层,统一住在本包。同一份 schema 既是表单描述,又是插件配置值类型的来源(ConfigOf)和运行时解析规则(parseConfig);渲染宿主(WebUI)与宿主政策(runtime 的配置同步)也用它消费 schema。

运行时只依赖同样零依赖的 @aalis/schema-i18n(给人看的文字是它的文字对象);对 @aalis/core 只有 type-only 锚点(以 peer 依赖声明)。字段类型可由其他 api 包经 declaration merging 扩展(如 api-llm 注入 'llm-ref')。

声明与读取

import { config, definePlugin, logger } from '@aalis/core';
import { defineConfig, parseConfig } from '@aalis/schema-config';

const configSchema = defineConfig({
  apiKey: { type: 'string', label: 'API Key', required: true, secret: true },
  port: { type: 'number', label: '端口', default: 8080, min: 1, max: 65535, integer: true },
  mode: {
    type: 'select',
    label: '模式',
    default: 'fast',
    options: [
      { label: '快', value: 'fast' },
      { label: '稳', value: 'safe' },
    ],
  },
});

export default definePlugin({
  name: '@your-scope/plugin-x',
  configSchema,
  uses: { config, logger },
  apply({ config, logger }) {
    const cfg = parseConfig(configSchema, config.use, logger.use);
    // cfg: { apiKey: string; port: number; mode: 'fast' | 'safe' }
  },
});
  • defineConfig(schema):运行时原样返回传入的对象(不冻结、不加任何属性,WebUI 读的就是这个活对象),类型层保留字面量,返回值可直接放进 definePlugin({ configSchema })。不要给变量标 ConfigSchema 类型注解,注解会把字面量拓宽掉,ConfigOf 只能推出宽类型。推出的类型里各属性只读、options 是字面量元组;需要在运行时改写 schema(如按已注册的服务补全选项)时,经 ConfigSchema 或 SchemaField 类型的引用改写。
  • ConfigOf<S>:从 schema 推导配置值类型。字段取 SchemaFieldTypes 里登记的取值类型(见下表);select / multiselect 有静态 options、没有 dynamicOptions 且 allowCustom 不为 true 时收窄为选项值的字面量联合,数字选项对应数字字面量。有 default 或 required: true 的字段必定存在,其余为可选;分组推为嵌套对象,总是存在;数组推为元素类型的数组,元素按同一规则推导。带 session 的顶层字段不在其中(见下文「可按会话覆盖的字段」)。
  • parseConfig(schema, raw, logger?):按 schema 解析插件拿到的配置,返回 ConfigOf 类型的新对象,不改入参。之后的夹紧、跨字段约束等核对作用于返回值。

给人看的文字

  • 字段、分组与数组的 label、description 和选项的 label 可以写字符串,也可以写 @aalis/schema-i18n 的文字对象(插件自己原文字典里的 t('…')),渲染宿主按读者语言渲染。default 是取值,不是文字。
  • validateConfig 的每条问题带 code(missing、type、not-option、pattern、format、integer、min、max、reserved-key、validate)与原因的文字对象 text;message 是它的原文,日志用。
  • configError(message) 收字符串或文字对象,收文字对象时挂上它(name 仍是 ConfigError),WebUI 按读者语言显示失败原因;missingConfigError 的说法本身是文字对象,note 同样可以是文字对象。
  • 本包自己的字典(ns @aalis/schema-config)收核心配置表单(CORE_CONFIG_SCHEMA)、校验原因与配置错误的说法。

字段类型

本包内置的中立类型。「判定」一列是 validateConfig 与 parseConfig 共用的逐字段规则:

type取值类型(ConfigOf)判定
string / textareastring字符串;有限数字按字符串收;声明了 pattern 时按正则匹配;声明了 format 时按路径写法判定
numbernumber有限数值;去掉首尾空白后非空、能完整解析为有限数的字符串按数字收;min / max / integer
booleanboolean类型,不做转换
select选项值的字面量联合,否则 string字符串或有限数字;有静态 options、没有 dynamicOptions 且 allowCustom 不为 true 时须是某个选项值
multiselect选项值字面量联合的数组,否则 string[]数组;元素同 select;按输入顺序保留。ordered: true 时先后有意义(如语言的先后):去掉重复项(按选项归位后的值比,不算问题),WebUI 给排序控件
liststring[]数组,元素按字符串判定;有序,允许重复(如命令行参数)
mapRecord<string, string>对象,每个值按字符串判定(如环境变量表)
  • 标量宽容:YAML 里不加引号的纯数字(QQ 号、口令、端口、环境变量值)在期望字符串的位置照常可用,加了引号的数字在期望数字的位置照常可用;parseConfig 返回换算后的值。
  • format:写宿主文件路径的 string / textarea 字段声明。'absolute-path' 只收绝对路径(/…、C:\… 或 \\…);'executable' 另收不含路径分隔符的命令名(按 PATH 查找),C:foo 这类带盘符的相对写法不算。判定忽略首尾空白,交给插件的值也去掉首尾空白;空串与只有空白都表示未填、不查(required 字段算缺失);只按写法判定、与宿主平台无关,Windows 形状的绝对路径在 POSIX 宿主上同样通过。配置里的相对路径没有可靠的基准(宿主进程的 cwd 不带语义),所以不收。
  • 选项即取值范围:选项按字符串比较,parseConfig 取选项声明的原值(WebUI 把数字选项存成字符串,读出时归位成数字)。
  • dynamicOptions / allowCustom:两者影响取值判定,由本包声明。dynamicOptions 是动态选项来源的服务名(WebUI 经 webui-server 调该服务的 listModels() 或等价方法获取选项),声明后静态 options 不再是取值范围,只查取值是字符串或数字;allowCustom 表示接受选项之外的值,不查是否在选项内:WebUI 给 multiselect 提供手动输入;select 用它表示静态选项不全(例如由插件在运行时补全选项的字段)。
  • 扩展字段类型:api 包经 declaration merging 往 SchemaFieldTypes 登记类型名与取值类型(如 'llm-ref': ModelRef),ConfigOf 据此推导。本包不按类型校验外来类型的取值,只调字段自己的 validate(见下文):validateConfig 放行,parseConfig 除把 '' 当作未配置外原样透传。

字段自己的校验与文字读写(validate / fromText / toText)

  • validate(value):合法返回 undefined,不合法返回原因(非空字符串或文字对象);抛错或返回别的值都按不合法,原因写「校验时出错」,不转述抛出的错。在类型与约束都过了之后调,拿换算后的取值(外来类型是原值)。validateConfig 报 invalid(code 为 validate),parseConfig 按无效处置(见下文),readFieldValue 给出原因。list、map、multiselect 有坏元素时,validateConfig 与 readFieldValue 只报元素、不调它,parseConfig 先丢坏元素、再拿剩下的值调它。外来类型的字段要核取值就写它。原因会原样进 parseConfig 的告警与拒绝激活的消息,不要在里面写取值(字段可能是密钥)。
  • fromText(text) / toText(value):文字与取值互换;内置类型不用写。
  • 三者是进程内函数,schema 经 JSON 下发时没有;线上类型是 WireSchemaField / WireConfigSchema。

可按会话覆盖的字段(session)

顶层字段写了 session(声明 SessionFieldSpec,成员由 @aalis/api-session-settings 并入)即可按会话覆盖:插件经会话设置的读取器按会话读它,改了不重载插件。isSessionField(entry) 判一个顶层条目是不是这种字段(分组、数组上写了不算,分组与数组元素里的字段不按它判)。

  • parseConfig 不交出这类字段、不补 default,配置里有这个键也不告警;ConfigOf 同样不含。
  • SessionFieldsOf<S>:这类字段经读取器读到的值。取值类型同 ConfigOf,onInvalid: 'error' 的为 unknown(坏值原样交出,由插件核);有 default 的必定存在,没有的为可选。
  • validateConfig、defaultsFrom、removeExtraFields 照常把它当 schema 字段:它的全局层就是配置文档里的值,作者默认照常写进文档。
  • readFieldValue(field, value):单个值的判定,与 validateConfig 同一套:合法给 { value }(换算后的取值;没配置的给 undefined),不合法给 { invalid }(集合里有坏元素也算)。不回落 default、不抛错、不拷贝。判的是取值,不是文档写法:密钥字段上的引用 validateConfig 放行,这里判为不合法。

密钥字段(secret)

secret: true 表示字段的值是凭据(API key、token、可能带账号密码的连接串),由本包声明,各宿主都按它处理:值不放在配置文档里,存进宿主的密钥存储(Node 宿主 @aalis/runtime 用数据目录的 secrets.yaml),文档里只写引用 { $secret: '<键>' }(类型 SecretRef),宿主把配置交给插件之前解析回原值,展示与回传都不给原值。标在 map、list 等非标量字段上时整个值算一个密钥。本包只声明词汇,不读写密钥存储。

  • isSecretRef(value):是不是引用,即只有 $secret 一个自有键、值为非空字符串的纯对象。只在 secret: true 的字段上有这层含义。
  • mapSecretFields(schema, config, fn):逐个处理配置里密钥字段上的值(顶层、分组内、数组元素内;值为 undefined 的跳过),fn 收到值与位置 SecretPosition(path 是点号路径,数组元素里的不带下标;inArray 表示在数组元素里),fn 返回 undefined 时删掉这个键。不改入参:有改动时返回浅拷贝(没动到的部分与入参共享),没有改动时返回入参本身,调用方要改写结果请先复制。宿主拆出、解析密钥与给呈现方去掉密钥都用它。
  • validateConfig 校验的是文档:密钥字段上的引用算合法,不查键在不在。
  • parseConfig 给插件用:密钥字段上仍是引用,说明宿主没能解析它(键不在密钥存储里),一律不可恢复,消息点名键名;不看 default 与 onInvalid,在数组元素里也让整次解析失败,不只丢这一条元素。回落默认值会悄悄连上别的东西(例如默认的本机数据库),所以不回落。
  • defaultsFrom 不产出密钥字段:派生结果会被宿主写进配置文档,密钥的默认值不进文档;parseConfig 照常补上默认值。

parseConfig 的处置

  • 缺失:undefined 与 null(YAML 裸键);required 字段另把 '' 当作缺失(WebUI 与脚手架用 '' 表示未填;声明了 format 的只有空白也算),外来类型的 '' 一律当作缺失(WebUI 给未选的 llm-ref 存 '')。有 default 用 default 的拷贝;无 default 且 required 不可恢复;否则省略该键。
  • 无效(不符合上表的判定,或字段的 validate 判为不合法):未声明 onInvalid 时保持宽容处置:有 default 回落并告警;无 default 且 required 不可恢复;否则省略并告警。字段声明 onInvalid: 'error' 时,显式无效值即使有 default 也抛 ConfigError;它不改变 required 的缺省含义,undefined / null 仍走上述缺省规则。
  • list / map / multiselect:默认坏元素、坏值逐个丢弃并告警,其余保留,剩下的值再过字段的 validate;稀疏数组的空位和 map 的保留键也属于坏成员。严格字段只要含一个坏成员就拒绝整个值,不返回部分结果。
  • 分组:值不是对象时默认告警,整组按空对象解析,子字段各取默认值。显式给非对象且分组含严格字段时拒绝;undefined / null 仍视为缺省。
  • 数组:值不是数组按无效处理,含严格元素字段时显式无效的数组值会被拒绝;元素不是对象、或元素内有不可恢复的字段时只丢这一条元素并告警,邻居元素照常解析;元素字段的默认值逐元素补齐(runtime 的配置同步不补数组元素的默认值)。
  • 整体配置:显式非对象且 schema 含严格字段时拒绝;undefined / null 仍视为缺省。严格错误说明字段路径与安全的类型或约束原因;集合的下标可定位,map 的键可能含密钥,因此错误只定位到 map 字段,不写用户值或键。
  • schema 外的键:丢弃并告警。runtime 已先裁掉顶层与分组内的,这里主要覆盖数组元素,以及不经 runtime 的宿主。
  • 不可恢复(顶层或分组内):缺失抛 missingConfigError,无效抛 configError,插件随之进 error 态,在 WebUI 保存或配置文件热重载后重试激活。密钥字段上没被解析的引用见上节,在任何位置都不可恢复。
  • 告警文案:配置项 <路径> <原因>,改用默认值 <JSON>、配置项 <路径> <原因>,已忽略、配置项 <数组路径>[i] 已忽略:<字段> <原因>。只写路径、类型名与约束,不写原值(可能是密钥);默认值会写出。

数组也可声明 onInvalid: 'error':非对象元素或元素内不可恢复的字段会拒绝整个数组,避免丢弃坏项后触发空数组的业务默认;元素内普通字段仍按自身的回落策略解析。未声明时继续逐项跳过,适合相互独立的 server 列表。

validateConfig(schema, config) 用同一份判定做只读检查,返回问题清单(kind 为 missing 或 invalid),不转换、不回填、不裁剪;它把 undefined / null 当作未配置,required 字段另把 ''(声明了 format 的字符串只有空白也算)当作缺失,与 parseConfig 同一判定;只在 required 且无 default 时报缺,声明了 default 的永远不算缺。onInvalid 改变 parseConfig 的处置,不改变校验器的判定或报告。宿主用它告警或拦截保存:runtime 的配置同步对启用中的插件打告警,WebUI 保存插件配置时拒绝本次新引入的 invalid。

list 与 map 的补充约定:

  • 默认值:default 分别写数组与对象,defaultsFrom 按值拷贝(secret: true 的除外)。deepMergeDefaults 合并默认值时递归合并对象(runtime 的配置同步与 WebUI 保存插件配置都用它),因此顶层或分组里的 map 会按键补齐:默认映射中的键总会回到用户配置里,删掉也无效。需要用户可删除的预置条目时,默认值写 {},由插件代码兜底。list 不参与合并,用户配置了就整体采用。
  • 未知字段裁剪:removeExtraFields 把 map 当作叶子整体保留,不按键裁剪。
  • 缺省 UI(WebUI 的 SchemaForm):两者都渲染为多行文本框。list 每行一项,空白行忽略;map 每行一条 KEY=VALUE,按第一个 = 切分,键去掉首尾空白,值原样保留,缺少 = 或键为空的行不保存。现有的数字、布尔项或值按字符串显示,编辑后写成字符串;其余无法按这套规则逐行表达的值(项或值本身含换行、list 含空白项、null、嵌套的数组或对象等)会让该字段只读显示,需在配置文件中编辑。旧版存下的空串按空列表 / 空映射显示,保存时写成 [] / {}。

消费方式:用宽区间,别用 caret

仓内五十余个包这样依赖本包(与 @aalis/core 的 peerDep 同一风格):

"dependencies": { "@aalis/schema-config": "workspace:>=0.9.0 <1.0.0" }

发布时该区间原样保留(实测 pnpm pack 产物即 >=0.9.0 <1.0.0),本地仍照常 link 到 workspace。

不要用 caret。 0.x 的 caret(^0.9.0 = >=0.9.0 <0.10.0)锁死 minor:本包加一个字段类型 就是一次 minor,届时所有已发布消费者的范围会拒收新版,node_modules 里出现两份副本——而 declaration merging 是按模块副本生效的,SchemaFieldTypes / SchemaField 的合并面会就此裂开 (一份合并了 'llm-ref',另一份没有)。宽区间让整个 0.x 段自由流动,加词汇零级联。

解析以调用方传入的值为准。宿主与 Core 的配置拷贝会移除 __proto__ / constructor / prototype 保留键,parseConfig 无法诊断在上游已被删除的条目;配置字典(含 env)不能使用这些键。

版本与发布记录

@aalis/schema-config 共发布 10 个版本,首次发布于 2026/08/04;过去 12 个月发布了 10 个版本。

版本发布日期距今
0.16.1latest2026/11/016 天前
0.16.02026/10/317 天前
0.15.02026/10/1127 天前
0.14.02026/10/011 个月前
0.13.12026/09/271 个月前
0.13.02026/09/271 个月前

关于 @aalis/schema-config 的常见问题

@aalis/schema-config 是什么?

配置表单 Schema 词汇契约包:ConfigSchema / SchemaField / SchemaGroup / SchemaArray 及 SchemaFieldTypes 扩展点;声明与读取插件配置的 defineConfig / ConfigOf / parseConfig;可按会话覆盖的字段 SessionFieldSpec / SessionFieldsOf / isSessionField,经 JSON 下发的 `WireSchemaFiel

如何安装 @aalis/schema-config?

在项目目录运行 npm install @aalis/schema-config,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:@aalis/core(>=0.21.0 <1.0.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@aalis/schema-config 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@aalis/schema-config 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@aalis/schema-config 可以商用吗?

@aalis/schema-config 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@aalis/schema-config 的下载量有多少?

根据 npm 官方下载统计,@aalis/schema-config 在 2026/10/05 – 2026/11/03 这 30 天内被下载 2,547 次,日均约 85 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@aalis/schema-config 还在维护吗?

最新版本 0.16.1 发布于 2026/11/01(6 天前);至今共发布 10 个版本,其中过去 12 个月发布了 10 个;首次发布于 2026/08/04。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。