@effected/config-file

中文文档
0.28.0发布于 2026/11/13MITTypeScriptES Module构建来源已验证近 30 天下载 1万

可编译的配置文件加载效果:JSON,JSONC,YAML和TOML代码,解析策略,并合并行为.

中文使用文档
文档目录9 节
  1. 为什么 @ effect/ config- file
  2. 安装
  3. 快开始
  4. 读取已知路径
  5. 拒绝密钥, 计划不知道
  6. 错误
  7. 编解码器
  8. 特征
  9. 许可证

@ action/ config- 文件

npm License: MIT Node.js %3E%3D24.11.0 TypeScript 7.0

可编译配置文件加载效果 。 宣告一个解锁链——一条明确的路径,从cwd向上行走,工作空间或基特根, /etc ——通过效果解码每个已发现的文件 Schema,并将结果与合并策略相结合。 JSON解码出盒;JSONC,YAML和TOML各取一个可选的引擎包. 编解码器,解析器和合并策略都是可插接的接合器,而故障的来由是带有结构化有效载荷而不是活口的被标记的错误,因此"在任何地方没有配置"与"我发现的配置被打破"是分道扬镳.

预1.0.0。 。 。 . 此软件包是 @effected/* 在稳定基础上建造的工具包 效应v4(effect ~4.1.0) 并仍在 0.x 发展。 稳定效果 制作一个工具箱 1.0.0 可能,不是自动的。 为了保持你的 effect 财务报告和审定财务报表 @effect/* 该工具包在线上构建和测试、安装 @effected/pnpm-plugin-effected。 。 。 .

稳定:不稳定. 此软件包的 API 表面尚未考虑 完成并可能改变 0.x 释放 定一个确切的版本——甚至 标记软件包 稳定 在此之前 1.0.0 可以引入突破性更改 和精确的针 变成一个类型检查错误 而不是一个 跑步时间惊喜。 完全政策: 释放策略。 。 。 .

为什么 @ effect/ config- file

配置加载是一类应用程序通常放弃的地方:库中找到文件,解析文件,验证文件,并报告其中每一个明显的失败,如一个不透明错误 reason 绳子。 这个包裹拒绝。 发现、阅读、解析、验证和持久性都因自己的有标记的错误而失败,而且每个错误都带有结构性原因——a ConfigValidationError 将计程树交给你,不是 String(ParseError)。解析器要求流入层的类别,而不是被丢弃,合并步骤报告每个贡献源,而不仅仅是第一个源,并且可以将装入的文件交给 Config 作为 v4 的存取器 ConfigProvider 在环境之下层出不穷.

安装

npm install @effected/config-file effect @effect/platform-node
pnpm add @effected/config-file effect @effect/platform-node

需要节点.js~~24.11.0. 那个安装覆盖了JSON。 要读取 JSONC, YAML 或 TOML, 添加匹配的引擎 :

pnpm add @effected/jsonc   # for @effected/config-file/jsonc
pnpm add @effected/yaml    # for @effected/config-file/yaml
pnpm add @effected/toml    # for @effected/config-file/toml

全体 @effected/* 软件包仅为无害环境管理:只公布出口地图 import 条件,所以 require() 包括解析 CJS 模式的工具 —— 与节点失败 ERR_PACKAGE_PATH_NOT_EXPORTED 而不是装入不存在的 CJS 建筑。 从ES模块导入.

effect v4 是一个对等依赖, 也一样 @effected/walker 财务报告和审定财务报表 @effected/glob - 转回原始的 upwardWalk, (中文(简体) ), workspaceRoot 财务报告和审定财务报表 gitRoot 解析器是建立在, 匹配它同行。 自动安装对等器的软件包管理器会将其拉入; 如果不安装, 将明确添加到您的清单中 . @effected/jsonc, (中文(简体) ), @effected/yaml 财务报告和审定财务报表 @effected/toml 为: 选项 对等者: ./jsonc, (中文(简体) ), ./yaml 财务报告和审定财务报表 ./toml 子路径加载它们,而包根从不这样做,所以一个JSON唯一的程序安装并加载其中任何一个. 它的一次性依赖是 @effected/files,它背面的原子写, 所以没有它拖入你的树 从外部 effect 财务报告和审定财务报表 @effected/*。 。 。 .

读取和写入文件需要 FileSystem (单位:千美元) Path 执行,一旦处于边缘即提供 @effect/platform-node 在节点上.

快开始

宣布一个计划,薄荷 服务类与之 ConfigFile.Service,并用 ConfigFile.layer。平台层一次提供,在边缘:

import { ConfigFile, ConfigResolver, JsonCodec, MergeStrategy } from "@effected/config-file";
import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem";
import * as NodePath from "@effect/platform-node/NodePath";
import { Effect, Layer, Schema } from "effect";

class AppShape extends Schema.Class<AppShape>("AppShape")({
  port: Schema.Number,
  host: Schema.String,
}) {}

class AppConfig extends ConfigFile.Service<AppConfig, AppShape>()("app/Config") {}

const AppConfigLive = ConfigFile.layer(AppConfig, {
  schema: AppShape,
  codec: JsonCodec,
  resolvers: [ConfigResolver.upwardWalk({ filename: ".apprc" })],
  strategy: MergeStrategy.firstMatch<AppShape>(),
});

const program = Effect.gen(function* () {
  const config = yield* AppConfig;
  return yield* config.load;
});

const PlatformLive = Layer.mergeAll(NodeFileSystem.layer, NodePath.layer);

Effect.runPromise(program.pipe(Effect.provide(AppConfigLive), Effect.provide(PlatformLive))).then(console.log);
// AppShape { port: 3000, host: "localhost" }

ConfigFile.layer 是图层返回 函数,而非一层:称其为"两次"会构建出两个独立的服务实例. 如上所述,将其结果与康斯特结合,并提供康斯特.

解决者按优先顺序协商,最高者优先. MergeStrategy.firstMatch 夺得冠军; MergeStrategy.layeredMerge 深相匹配的每个源, 更高优先级的密钥覆盖低等密钥 :

import { ConfigFile, ConfigResolver, MergeStrategy } from "@effected/config-file";
import { YamlCodec } from "@effected/config-file/yaml";
import { Schema } from "effect";

class Settings extends Schema.Class<Settings>("Settings")({ port: Schema.Number }) {}
class SettingsConfig extends ConfigFile.Service<SettingsConfig, Settings>()("app/Settings") {}

export const SettingsLive = ConfigFile.layer(SettingsConfig, {
  schema: Settings,
  codec: YamlCodec,
  resolvers: [
    ConfigResolver.upwardWalk({ filename: ".apprc.yaml" }),
    ConfigResolver.workspaceRoot({ filename: ".apprc.yaml" }),
    ConfigResolver.systemEtc({ app: "myapp", filename: "config.yaml" }),
  ],
  strategy: MergeStrategy.layeredMerge<Settings>(),
});

读取已知路径

并不是每个呼叫者都有一个配置文件——有些人只有一条已经保证过的呼叫者路径,比如CLI's --config 旗帜. ConfigFile.read 是一个从服务、层和解析器链中取出的一发子弹:读取、解码和验证一个单一路径,每个调用时命名的 schema 和 codec 而不是绑定在一个服务类中:

import { ConfigFile, JsonCodec } from "@effected/config-file";
import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem";
import { Effect, Schema } from "effect";

class MyConfig extends Schema.Class<MyConfig>("MyConfig")({ port: Schema.Number }) {}

const program = ConfigFile.read("./app.config.json", { schema: MyConfig, codec: JsonCodec });

Effect.runPromise(program.pipe(Effect.provide(NodeFileSystem.layer))).then(console.log);
// MyConfig { port: 3000 }

它刻意只读和无发现——没有解析链,没有 save编号 :update联系 ConfigFile.layer 任何时刻都是被通缉的.

拒绝密钥, 计划不知道

Effect的解码器默认忽略了未知的密钥,对于一个配置加载器来说,这意味着一个打字"d"区段被静默地放下. 用户不会出错,他们写的设定没有效果,运行中没有任何内容说明原因. parseOptions 线程解码选项进入装入器执行的每个解码器 ConfigFile.layer 财务报告和审定财务报表 ConfigFile.read 类似:

import { ConfigFile, ConfigResolver, JsonCodec, MergeStrategy } from "@effected/config-file";
import { Schema } from "effect";

class Settings extends Schema.Class<Settings>("Settings")({ port: Schema.Number }) {}
class SettingsConfig extends ConfigFile.Service<SettingsConfig, Settings>()("app/Settings") {}

export const SettingsLive = ConfigFile.layer(SettingsConfig, {
  schema: Settings,
  codec: JsonCodec,
  resolvers: [ConfigResolver.upwardWalk({ filename: ".apprc" })],
  strategy: MergeStrategy.firstMatch<Settings>(),
  parseOptions: { onExcessProperty: "error", errors: "all" },
});
// A file carrying `{ "port": 3000, "prot": 3001 }` now fails with a
// ConfigValidationError whose issue tree names the offending path.

这个 validate 选项无法表示:它运行在已解码的值上,此时多余的密钥已经消失。 对 onExcessProperty: "error" 与 errors: "all" ——解码器报告只有第一个问题,否则,所以一个有3个打字符的文件会花费用户三个固定和重跑周期的费用. 额外工作只发生在已经失败的文档上 .

一个故意承认通过段的图谋 一直在工作 "error" - 但知道为什么,因为形状暗示相反:a Schema.StructWithRest 休养 切换过量的检查 完全的不只是钥匙 其余的封面。 没有休息的 Structs 严格地独立地停留,所以严格性是由每个关卡而不是每个密钥决定的. 选择 parseOptions 改变不了任何变化,这就使这个问题成为每个负载者的决定,而不是迁移.

错误

每一次失败都是被标记的错误 Effect.catchTag。标签存在,以便回收可以不同:

标记手段恢复
ConfigFileNotFoundError解锁链没有匹配。 卡莉丝 searched,解析器名称探测,和 candidates在磁盘上检查的路径 .倒退到默认——一个常常不是错误的失败. loadOrDefault 帮你处理.
ConfigFileReadError找到文件但无法读取 。 卡莉丝 path 结构 cause。 。 。 .通常致命:文件存在,进程无法读取. 检查权限 .
ConfigFileWriteError无法写入文件 。 卡莉丝 path 结构 cause。 。 。 .在别处重试, 或者表面给用户 .
ConfigDefaultPathMissingErrorsave 或者说 update 被调用到一个没有 defaultPath。 。 。 .接线错误, 不是数据条件 。 修补层层,或调用 write 以一条明确的道路.
ConfigValidationError文件没有满足计划 或呼叫者提供 validate 拒绝。 承载结构化 issue 树和可选 path。 。 。 .报告问题; 不要运行无法验证的配置 .
ConfigCodecError解码器无法解析或字符串 。 卡莉丝 codec, (中文(简体) ), operation 结构 cause。 。 。 .文件腐败了 报道与因.
ConfigMigrationError版本迁移失败 。 卡莉丝 version, (中文(简体) ), name, (中文(简体) ), phase 结构 cause。 。 。 .报告该步骤失败; 磁盘上的配置未被更改 .
ConfigEncryptionError加密、解密、密钥开发或基数64步骤失败 。 卡莉丝 phase 结构 cause。 。 。 .错误的密码句和腐败的信封都在这里;检查 phase。 。 。 .

ConfigLoadError, (中文(简体) ), ConfigReadError, (中文(简体) ), ConfigEncodeError, (中文(简体) ), ConfigWriteError, (中文(简体) ), ConfigSaveError 财务报告和审定财务报表 ConfigUpdateError 是出口工会,确切地点出每种方法可能产生的失败 ConfigEncodeError 实值 ConfigWriteError 减 ConfigFileWriteError,因为 encode 永远不要碰磁盘。 抓住一个标签 缩小工会, 留下其余的传播:

import type { ConfigFileShape } from "@effected/config-file";
import { Effect, Schema } from "effect";

class AppShape extends Schema.Class<AppShape>("AppShape")({ port: Schema.Number }) {}

const fallback = new AppShape({ port: 3000 });

// `load` fails with ConfigLoadError. Handling the not-found tag leaves
// ConfigReadError — the file-is-broken failures, which we let propagate.
export const loadOrFallback = (config: ConfigFileShape<AppShape>) =>
  config.load.pipe(Effect.catchTag("ConfigFileNotFoundError", () => Effect.succeed(fallback)));

编解码器

包裹中有4艘解码器飞船,每艘都有独立的命名导出. JsonCodec 来源于软件包根;另外三个各有各自的子路径和一个可选的引擎对等端,可以安装在旁边:

代码c格式从发动机
JsonCodec贾森@effected/config-file主机 JSON 全球, 完全没有解析器
JsoncCodec约克@effected/config-file/jsonc@effected/jsonc
YamlCodec也门@effected/config-file/yaml@effected/yaml
TomlCodec移动@effected/config-file/toml@effected/toml

您只安装并装入您命名的解析器 。 根从未到达子路径,所以一个JSON唯一的应用程序没有安装解析器,也没有船. 每个子路径只到达自己的引擎, 解码器从来没有被收集到一个命名空间对象, 所以导入 TomlCodec 从未提及 YAML 或 JSONC 绑定 .

编译. EncryptedCodec 用 AES-GCM 包入任何编码器,以及 ConfigMigration.make 将任何解码器包裹起来,这样解码的内容会被提升到最新的版本。 每个 宽度 错误通道,而不是将其故障平整到内置编码器的错误,以及装饰的编码器插件进入 ConfigFile.layer, (中文(简体) ), ConfigFile.testLayer 财务报告和审定财务报表 ConfigFile.read 象其他的一样,他们的配主将因它而扩大。 一个服务密钥将拓宽错误命名为它的第三种参数 :

import type { ConfigCodec } from "@effected/config-file";
import {
  ConfigFile,
  ConfigMigration,
  ConfigResolver,
  EncryptedCodec,
  EncryptedCodecKey,
  JsonCodec,
  MergeStrategy,
} from "@effected/config-file";
import { Effect, Schema } from "effect";

const migrating = ConfigMigration.make({
  codec: JsonCodec,
  migrations: [
    {
      version: 2,
      name: "add-port",
      up: (raw) => Effect.succeed({ ...(raw as Record<string, unknown>), port: 8080 }),
    },
  ],
});

// `parse` now fails with ConfigCodecError | ConfigMigrationError | ConfigEncryptionError.
export const secret = EncryptedCodec(migrating, EncryptedCodecKey.fromPassphrase("hunter2", new Uint8Array(16)));

const Settings = Schema.Struct({ port: Schema.Number });
// Read back from the codec value: ConfigCodecError | ConfigMigrationError | ConfigEncryptionError.
type SecretError = ConfigCodec.ErrorOf<typeof secret>;

class SecretSettings extends ConfigFile.Service<SecretSettings, typeof Settings.Type, SecretError>()(
  "app/SecretSettings",
) {}

export const SecretSettingsLive = ConfigFile.layer(SecretSettings, {
  schema: Settings,
  codec: secret,
  resolvers: [ConfigResolver.explicitPath("./settings.enc")],
  strategy: MergeStrategy.firstMatch(),
});

离开第三种参数和 ConfigFile.layer 调用是一个编译错误: 该服务的方法会随着一个 ConfigEncryptionError 其类型从不命名。 信封的基数64由核心解码 effect/encoding/Base64,所以周围的白空间是被容忍的,而里面的白空间,或者缺失的粘贴则失败了 ConfigEncryptionError 阶段 "encoding"。 。 。 .

特征

  • ConfigFile.Service 编号 : ConfigFile.layer 编号 : ConfigFile.testLayer ——每层服务类及其分层. testLayer 种子文件输入临时目录,并连接 真实 因此,试验要锻炼实际的管道,而不是能够从管道中漂出的一个支点.
  • parseOptions ——解码选项被串入到每个解码层和层上 read。 。 。 . onExcessProperty: "error" 只有这样才能报告 故意删除的打字段或字段.
  • ConfigFile.read ——单发从服务中逃出:读取,解码并验证一个明确路径,每个调用命名的 schema and codec,没有解析器链和写出路径.
  • encode 编号 : write 编号 : save 编号 : update ——写道. encode(value, options?) 返回已序列化文本 a write 将不写入、不发布事件或不指定错误路径: --dry-run 旗帜. write 采取一个明确的路径 呼叫者保证; save 财务报告和审定财务报表 update 解析层 defaultPath 并创建其父目录. encode 财务报告和审定财务报表 write 接受 { header },在文件上方正本加一行 #:schema https://… 命令 atop a TOML 文件, 例如 。 调用者以目标格式拥有其有效性; JSON 没有注释语法, 所以在 JsonCodec 生成无法解析的文件.
  • ConfigResolver —— 说 explicitPath, (中文(简体) ), staticDir, (中文(简体) ), upwardWalk, (中文(简体) ), workspaceRoot, (中文(简体) ), gitRoot 财务报告和审定财务报表 systemEtc。解析器的错误通道是 never 通过合同: 每一个文件系统失效都变为 Option.none(),因此一个无法读取的层次永远不会中止链条.
  • MergeStrategy —— 说 firstMatch 财务报告和审定财务报表 layeredMerge,以优先排序合并已发现的来源.
  • JsonCodec (基层), JsoncCodec (单位:千美元)./jsonc, (中文(简体) ), YamlCodec (单位:千美元)./yaml, (中文(简体) ), TomlCodec (单位:千美元)./toml)——JSON,JSONC,YAML和TOML,导出独立并被子路径分割,因此一个未使用格式的引擎既不被安装也不被装入.
  • ConfigCodec 编号 : EncryptedCodec 编号 : ConfigMigration ——可插接的可编码接合器,在其出错类型中通用,因此装饰器会拓宽而不是平整. ConfigCodec 是界面:通过满足它来带来自己的格式.
  • ConfigEvents ——选入 PubSub 联 合 国 ConfigEvent,在省略时为零成本:否 events 选项表示完全没有上下文。 失败事件带有结构化的打出错误, 绝不是 reason 绳子.
  • asConfigProvider 编号 : layerConfigProvider - 以 v4 形式曝光已装入、已验证的文件 ConfigProvider,在环境下分层,所以环境变量会覆盖它部署的文件.

许可证

麻省理工学院

版本与发布记录

@effected/config-file 共发布 54 个版本,首次发布于 2026/07/17;过去 12 个月发布了 54 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.28.0latest2026/11/13今天
0.27.12026/11/112 天前
0.27.02026/11/102 天前
0.26.02026/11/094 天前
0.25.02026/11/067 天前
0.24.02026/11/0310 天前

关于 @effected/config-file 的常见问题

@effected/config-file 是什么?

可编译的配置文件加载效果:JSON,JSONC,YAML和TOML代码,解析策略,并合并行为.

如何安装 @effected/config-file?

在项目目录运行 npm install @effected/config-file,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:effect(~4.1.0)、@effected/glob(^0.22.0)、@effected/walker(^0.29.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@effected/config-file 支持 TypeScript 吗?

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

@effected/config-file 支持 ESM 还是 CommonJS?

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

@effected/config-file 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=24.11.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@effected/config-file 可以商用吗?

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

@effected/config-file 的下载量有多少?

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

@effected/config-file 还在维护吗?

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