# @effected/config-file 中文文档

规范地址：https://npmcn.com/package/%40effected/config-file/

版本：0\.28\.0
npm 元数据更新时间：2026-11-12T19:03:28.920Z
中文译文更新时间：2026-11-13T08:51:00.055Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40effected/config-file
原文对照：https://npmcn.com/package/%40effected/config-file/?lang=original
项目主页：https://github.com/spencerbeggs/effected/tree/main/packages/config-file#readme
源码仓库：https://github.com/spencerbeggs/effected

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：170 kB，27 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=24\.11\.0
- 运行依赖：1 个
- peer 依赖：effect ~4\.1\.0，@effected/glob ^0\.22\.0，@effected/toml ^0\.20\.0，@effected/yaml ^0\.30\.0，@effected/jsonc ^0\.25\.0，@effected/walker ^0\.29\.0
- 首次发布：2026-07-16T17:31:35.198Z
- 最新版本发布：2026-11-12T19:03:28.419Z
- 近 30 天下载量：10109（2026-10-13 至 2026-11-11，npm 官方下载统计）
- 已发布版本数：54（近 12 个月 54 个）

## 中文简介

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

## 安装

```sh
npm install @effected/config-file
```

也可以使用：`pnpm add @effected/config-file`、`yarn add @effected/config-file`、`bun add @effected/config-file`

## README

# @ action/ config- 文件

[![npm](https://img.shields.io/npm/v/@effected%2Fconfig-file?label=npm&color=cb3837)](https://www.npmjs.com/package/@effected/config-file) [![License: MIT](https://img.shields.io/badge/License-MIT-4caf50.svg)](https://opensource.org/licenses/MIT) [![Node.js %3E%3D24.11.0](https://img.shields.io/badge/Node.js-%3E%3D24.11.0-5fa04e.svg)](https://nodejs.org/) [![TypeScript 7.0](https://img.shields.io/badge/TypeScript-7.0-3178c6.svg)](https://www.typescriptlang.org/)

可编译配置文件加载效果 。 宣告一个解锁链——一条明确的路径,从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`](https://www.npmjs.com/package/@effected/pnpm-plugin-effected)。 。 。 .
> 
> **稳定:不稳定.** 此软件包的 API 表面尚未考虑 完成并可能改变 `0.x` 释放 定一个确切的版本——甚至 标记软件包 _稳定_ 在此之前 `1.0.0` 可以引入突破性更改 和精确的针 变成一个类型检查错误 而不是一个 跑步时间惊喜。 完全政策: [释放策略](https://github.com/spencerbeggs/effected#release-strategy)。 。 。 .

## 为什么 @ effect/ config- file

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

## 安装

```bash
npm install @effected/config-file effect @effect/platform-node
```

```bash
pnpm add @effected/config-file effect @effect/platform-node
```

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

```bash
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`。平台层一次提供,在边缘:

```ts
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` 深相匹配的每个源, 更高优先级的密钥覆盖低等密钥 :

```ts
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 而不是绑定在一个服务类中:

```ts
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` 类似:

```ts
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`。 。 。 . | 在别处重试, 或者表面给用户 . |
| `ConfigDefaultPathMissingError` | `save` 或者说 `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` 永远不要碰磁盘。 抓住一个标签 缩小工会, 留下其余的传播:

```ts
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` 象其他的一样,他们的配主将因它而扩大。 一个服务密钥将拓宽错误命名为它的第三种参数 :

```ts
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`,在环境下分层,所以环境变量会覆盖它部署的文件.

## 许可证

[麻省理工学院](https://npmcn.com/package/%40effected/config-file/LICENSE)

## 常见问题

### @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 和提交记录判断。

