@effected/config-file 0.19.1 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -13
- package/index.d.ts +8 -55
- package/index.js +1 -4
- package/jsonc.d.ts +17 -0
- package/jsonc.js +3 -0
- package/package.json +29 -3
- package/toml.d.ts +21 -0
- package/toml.js +3 -0
- package/yaml.d.ts +16 -0
- package/yaml.js +3 -0
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://nodejs.org/)
|
|
6
6
|
[](https://www.typescriptlang.org/)
|
|
7
7
|
|
|
8
|
-
Composable config file loading for Effect. Declare a resolver chain — an explicit path, an upward walk from the cwd, the workspace or git root, `/etc` — decode every discovered file through an Effect `Schema`, and combine the results with a merge strategy. JSON
|
|
8
|
+
Composable config file loading for Effect. Declare a resolver chain — an explicit path, an upward walk from the cwd, the workspace or git root, `/etc` — decode every discovered file through an Effect `Schema`, and combine the results with a merge strategy. JSON decodes out of the box; JSONC, YAML and TOML each take one optional engine package. Codecs, resolvers and merge strategies are pluggable seams, and failures arrive as tagged errors carrying structured payloads rather than prose, so "no config anywhere" is routable separately from "the config I found is broken".
|
|
9
9
|
|
|
10
10
|
> **Pre-`1.0.0`.** This package is part of the `@effected/*` kit, built on stable
|
|
11
11
|
> Effect v4 (`effect` `^4.0.0`) and still in `0.x` development. Stable Effect
|
|
@@ -33,11 +33,17 @@ npm install @effected/config-file effect @effect/platform-node
|
|
|
33
33
|
pnpm add @effected/config-file effect @effect/platform-node
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
Requires Node.js >=24.11.0.
|
|
36
|
+
Requires Node.js >=24.11.0. That install covers JSON. To read JSONC, YAML or TOML, add the matching engine:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pnpm add @effected/jsonc # for @effected/config-file/jsonc
|
|
40
|
+
pnpm add @effected/yaml # for @effected/config-file/yaml
|
|
41
|
+
pnpm add @effected/toml # for @effected/config-file/toml
|
|
42
|
+
```
|
|
37
43
|
|
|
38
44
|
All `@effected/*` packages are ESM-only: the exports maps publish only `import` conditions, so `require()` — including tools that resolve in CJS mode — fails with Node's `ERR_PACKAGE_PATH_NOT_EXPORTED` rather than loading a CJS build that does not exist. Import from an ES module.
|
|
39
45
|
|
|
40
|
-
`effect` v4 is a peer dependency, and so are `@effected/
|
|
46
|
+
`effect` v4 is a peer dependency, and so are `@effected/walker` and `@effected/glob` — the traversal primitive the `upwardWalk`, `workspaceRoot` and `gitRoot` resolvers are built on, and the matcher it peers on. Package managers that install peers automatically will pull them in; add them to your manifest explicitly if yours does not. `@effected/jsonc`, `@effected/yaml` and `@effected/toml` are **optional** peers: only the `./jsonc`, `./yaml` and `./toml` subpaths load them, and the package root never does, so a JSON-only program installs and loads none of them. The package declares no runtime dependencies of its own, so nothing it drags into your tree comes from outside `effect` and `@effected/*`.
|
|
41
47
|
|
|
42
48
|
Reading and writing files needs a `FileSystem` and a `Path` implementation, provided once at the edge — from `@effect/platform-node` on Node.
|
|
43
49
|
|
|
@@ -80,7 +86,8 @@ Effect.runPromise(program.pipe(Effect.provide(AppConfigLive), Effect.provide(Pla
|
|
|
80
86
|
Resolvers are consulted in priority order, highest first. `MergeStrategy.firstMatch` takes the winner; `MergeStrategy.layeredMerge` deep-merges every source that matched, with higher-priority keys overwriting lower ones:
|
|
81
87
|
|
|
82
88
|
```ts
|
|
83
|
-
import { ConfigFile, ConfigResolver, MergeStrategy
|
|
89
|
+
import { ConfigFile, ConfigResolver, MergeStrategy } from "@effected/config-file";
|
|
90
|
+
import { YamlCodec } from "@effected/config-file/yaml";
|
|
84
91
|
import { Schema } from "effect";
|
|
85
92
|
|
|
86
93
|
class Settings extends Schema.Class<Settings>("Settings")({ port: Schema.Number }) {}
|
|
@@ -176,16 +183,16 @@ export const loadOrFallback = (config: ConfigFileShape<AppShape>) =>
|
|
|
176
183
|
|
|
177
184
|
## Codecs
|
|
178
185
|
|
|
179
|
-
Four codecs ship in the package, each a free-standing named export:
|
|
186
|
+
Four codecs ship in the package, each a free-standing named export. `JsonCodec` comes from the package root; the other three each have their own subpath and an optional engine peer to install beside it:
|
|
180
187
|
|
|
181
|
-
| Codec | Format | Engine |
|
|
182
|
-
| ----- | ------ | ------ |
|
|
183
|
-
| `JsonCodec` | JSON | the host `JSON` global, no parser at all |
|
|
184
|
-
| `JsoncCodec` | JSONC | `@effected/jsonc` |
|
|
185
|
-
| `YamlCodec` | YAML | `@effected/yaml` |
|
|
186
|
-
| `TomlCodec` | TOML | `@effected/toml` |
|
|
188
|
+
| Codec | Format | Import from | Engine |
|
|
189
|
+
| ----- | ------ | ----------- | ------ |
|
|
190
|
+
| `JsonCodec` | JSON | `@effected/config-file` | the host `JSON` global, no parser at all |
|
|
191
|
+
| `JsoncCodec` | JSONC | `@effected/config-file/jsonc` | `@effected/jsonc` |
|
|
192
|
+
| `YamlCodec` | YAML | `@effected/config-file/yaml` | `@effected/yaml` |
|
|
193
|
+
| `TomlCodec` | TOML | `@effected/config-file/toml` | `@effected/toml` |
|
|
187
194
|
|
|
188
|
-
|
|
195
|
+
You install and load only the parser you name. The root never reaches a subpath, so a JSON-only application installs no parser and ships none. Each subpath reaches only its own engine, and the codecs are never collected into a namespace object, so importing `TomlCodec` never references the YAML or JSONC bindings.
|
|
189
196
|
|
|
190
197
|
Codecs compose. `EncryptedCodec` wraps any codec with AES-GCM, and `ConfigMigration.make` wraps any codec so parsed content is brought up to the latest version. Each *widens* the error channel rather than flattening its failures into the inner codec's error, and a decorated codec plugs into `ConfigFile.layer`, `ConfigFile.testLayer` and `ConfigFile.read` like any other: their error unions widen with it. A service key names the widened error as its third type argument:
|
|
191
198
|
|
|
@@ -242,7 +249,7 @@ Leave the third type argument off and the `ConfigFile.layer` call is a compile e
|
|
|
242
249
|
- `encode` / `write` / `save` / `update` — the write path. `encode(value, options?)` returns the serialized text a `write` would put on disk without writing, emitting an event or naming a path in its errors: the primitive behind a `--dry-run` flag. `write` takes an explicit path the caller vouches for; `save` and `update` resolve the layer's `defaultPath` and create its parent directory. `encode` and `write` accept `{ header }`, text prepended verbatim above the document with exactly one newline between — a `#:schema https://…` directive atop a TOML file, say. The caller owns its validity in the target format; JSON has no comment syntax, so a header on `JsonCodec` yields an unparseable file by construction.
|
|
243
250
|
- `ConfigResolver` — `explicitPath`, `staticDir`, `upwardWalk`, `workspaceRoot`, `gitRoot` and `systemEtc`. A resolver's error channel is `never` by contract: every filesystem failure becomes `Option.none()`, so one unreadable tier never aborts the chain.
|
|
244
251
|
- `MergeStrategy` — `firstMatch` and `layeredMerge`, combining discovered sources in priority order.
|
|
245
|
-
- `JsonCodec
|
|
252
|
+
- `JsonCodec` (root), `JsoncCodec` (`./jsonc`), `YamlCodec` (`./yaml`), `TomlCodec` (`./toml`) — JSON, JSONC, YAML and TOML, exported free-standing and split by subpath so an unused format's engine is neither installed nor loaded.
|
|
246
253
|
- `ConfigCodec` / `EncryptedCodec` / `ConfigMigration` — a pluggable codec seam, generic in its error type so decorators widen rather than flatten. `ConfigCodec` is the interface: bring your own format by satisfying it.
|
|
247
254
|
- `ConfigEvents` — an opt-in `PubSub` of `ConfigEvent`, honestly zero-cost when omitted: no `events` option means no context lookup at all. Failure events carry the structured typed error, never a `reason` string.
|
|
248
255
|
- `asConfigProvider` / `layerConfigProvider` — expose a loaded, validated document as a v4 `ConfigProvider`, layered beneath the ambient one so an environment variable overrides the file it was deployed with.
|
package/index.d.ts
CHANGED
|
@@ -48,13 +48,14 @@ export declare class ConfigCodecError extends ConfigCodecError_base {
|
|
|
48
48
|
* decorator codecs such as `EncryptedCodec` and `ConfigMigration.make` widen it
|
|
49
49
|
* rather than flattening their own failures into a string.
|
|
50
50
|
*
|
|
51
|
-
* The four built-in codecs
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
51
|
+
* The four built-in codecs are free-standing named exports, one per module,
|
|
52
|
+
* and are deliberately never collected into a namespace object. `JsonCodec`
|
|
53
|
+
* ships from the package root; `JsoncCodec`, `YamlCodec` and `TomlCodec` ship
|
|
54
|
+
* from the `./jsonc`, `./yaml` and `./toml` subpaths, whose engines are
|
|
55
|
+
* optional peers the root never reaches. Collecting them would make this module
|
|
56
|
+
* a dispatch table: referencing it at all would reach every codec, and every
|
|
57
|
+
* codec would reach its parsing engine, so importing the JSON codec alone would
|
|
58
|
+
* drag the JSONC, YAML and TOML engines into the bundle.
|
|
58
59
|
*
|
|
59
60
|
* @public
|
|
60
61
|
*/
|
|
@@ -1464,53 +1465,5 @@ export declare function EncryptedCodec<E>(inner: ConfigCodec<E>, keySource: Encr
|
|
|
1464
1465
|
* @public
|
|
1465
1466
|
*/
|
|
1466
1467
|
export declare const JsonCodec: ConfigCodec;
|
|
1467
|
-
/**
|
|
1468
|
-
* A `ConfigCodec` backed by `@effected/jsonc`: JSON with comments and
|
|
1469
|
-
* trailing commas.
|
|
1470
|
-
*
|
|
1471
|
-
* @remarks
|
|
1472
|
-
* `@effected/jsonc` does not expose a `stringify` — its schema layer's encode
|
|
1473
|
-
* direction is `JSON.stringify` (comments never survive a round-trip encode;
|
|
1474
|
-
* see `Jsonc.fromString`'s remarks), so `stringify` here calls `JSON.stringify`
|
|
1475
|
-
* directly and wraps a thrown defect the same way `JsonCodec` does.
|
|
1476
|
-
* Both directions preserve the underlying failure structurally in `cause` —
|
|
1477
|
-
* never stringified.
|
|
1478
|
-
*
|
|
1479
|
-
* @public
|
|
1480
|
-
*/
|
|
1481
|
-
export declare const JsoncCodec: ConfigCodec;
|
|
1482
|
-
/**
|
|
1483
|
-
* A `ConfigCodec` backed by `@effected/toml`.
|
|
1484
|
-
*
|
|
1485
|
-
* @remarks
|
|
1486
|
-
* `@effected/toml`'s input hardening — a nesting-depth cap on arrays and
|
|
1487
|
-
* inline tables, enforced independently on both the parse and stringify
|
|
1488
|
-
* sides — fails through the typed error channel, so a hostile config file
|
|
1489
|
-
* surfaces as a `ConfigCodecError` rather than crashing the process. Both
|
|
1490
|
-
* directions preserve the underlying failure structurally in `cause` —
|
|
1491
|
-
* never stringified.
|
|
1492
|
-
*
|
|
1493
|
-
* Stringify is genuinely fallible beyond hostile input: TOML has no null,
|
|
1494
|
-
* so a document carrying `null` (or any other unrepresentable value, an
|
|
1495
|
-
* out-of-int64-range `bigint`, a circular reference) fails with a
|
|
1496
|
-
* `ConfigCodecError` whose `cause` is the structured `TomlStringifyError`.
|
|
1497
|
-
*
|
|
1498
|
-
* @public
|
|
1499
|
-
*/
|
|
1500
|
-
export declare const TomlCodec: ConfigCodec;
|
|
1501
|
-
/**
|
|
1502
|
-
* A `ConfigCodec` backed by `@effected/yaml`.
|
|
1503
|
-
*
|
|
1504
|
-
* @remarks
|
|
1505
|
-
* `@effected/yaml`'s input hardening — an alias-expansion budget guarding
|
|
1506
|
-
* against "billion laughs" alias bombs, and a collection-nesting depth cap —
|
|
1507
|
-
* fails through the typed error channel, so a hostile config file surfaces
|
|
1508
|
-
* as a `ConfigCodecError` rather than crashing the process. Both directions
|
|
1509
|
-
* preserve the underlying failure structurally in `cause` — never
|
|
1510
|
-
* stringified.
|
|
1511
|
-
*
|
|
1512
|
-
* @public
|
|
1513
|
-
*/
|
|
1514
|
-
export declare const YamlCodec: ConfigCodec;
|
|
1515
1468
|
export type { ConfigCodec, ConfigEncodeError, ConfigEncodeOptions, ConfigEventsShape, ConfigFileMigration, ConfigFileOptions, ConfigFileShape, ConfigFileTestOptions, ConfigLoadError, ConfigMatch, ConfigMigrationOptions, ConfigProbe, ConfigReadError, ConfigReadOptions, ConfigSaveError, ConfigSource, ConfigUpdateError, ConfigWriteError, LayerConfigProviderOptions, NonEmptySources, UpwardWalkOptions };
|
|
1516
1469
|
//# sourceMappingURL=index.d.ts.map
|
package/index.js
CHANGED
|
@@ -6,9 +6,6 @@ import { ConfigMigration, ConfigMigrationError, VersionAccess } from "./ConfigMi
|
|
|
6
6
|
import { asConfigProvider, layerConfigProvider } from "./ConfigProvider.js";
|
|
7
7
|
import { ConfigEncryptionError, EncryptedCodec, EncryptedCodecKey } from "./EncryptedCodec.js";
|
|
8
8
|
import { JsonCodec } from "./JsonCodec.js";
|
|
9
|
-
import { JsoncCodec } from "./JsoncCodec.js";
|
|
10
9
|
import { MergeStrategy } from "./MergeStrategy.js";
|
|
11
|
-
import { TomlCodec } from "./TomlCodec.js";
|
|
12
|
-
import { YamlCodec } from "./YamlCodec.js";
|
|
13
10
|
|
|
14
|
-
export { ConfigCodecError, ConfigDefaultPathMissingError, ConfigEncryptionError, ConfigEvent, ConfigEventPayload, ConfigEvents, ConfigFile, ConfigFileNotFoundError, ConfigFileReadError, ConfigFileWriteError, ConfigMigration, ConfigMigrationError, ConfigResolver, ConfigSourceRef, ConfigValidationError, EncryptedCodec, EncryptedCodecKey, JsonCodec,
|
|
11
|
+
export { ConfigCodecError, ConfigDefaultPathMissingError, ConfigEncryptionError, ConfigEvent, ConfigEventPayload, ConfigEvents, ConfigFile, ConfigFileNotFoundError, ConfigFileReadError, ConfigFileWriteError, ConfigMigration, ConfigMigrationError, ConfigResolver, ConfigSourceRef, ConfigValidationError, EncryptedCodec, EncryptedCodecKey, JsonCodec, MergeStrategy, VersionAccess, asConfigProvider, layerConfigProvider };
|
package/jsonc.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ConfigCodec } from "@effected/config-file";
|
|
2
|
+
/**
|
|
3
|
+
* A `ConfigCodec` backed by `@effected/jsonc`: JSON with comments and
|
|
4
|
+
* trailing commas.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* `@effected/jsonc` does not expose a `stringify` — its schema layer's encode
|
|
8
|
+
* direction is `JSON.stringify` (comments never survive a round-trip encode;
|
|
9
|
+
* see `Jsonc.fromString`'s remarks), so `stringify` here calls `JSON.stringify`
|
|
10
|
+
* directly and wraps a thrown defect the same way `JsonCodec` does.
|
|
11
|
+
* Both directions preserve the underlying failure structurally in `cause` —
|
|
12
|
+
* never stringified.
|
|
13
|
+
*
|
|
14
|
+
* @public
|
|
15
|
+
*/
|
|
16
|
+
export declare const JsoncCodec: ConfigCodec;
|
|
17
|
+
//# sourceMappingURL=jsonc.d.ts.map
|
package/jsonc.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effected/config-file",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Composable config file loading for Effect: JSON, JSONC, YAML and TOML codecs, resolution strategies, and merge behaviors.",
|
|
6
6
|
"keywords": [
|
|
@@ -40,16 +40,42 @@
|
|
|
40
40
|
"import": "./index.js",
|
|
41
41
|
"default": "./index.js"
|
|
42
42
|
},
|
|
43
|
+
"./jsonc": {
|
|
44
|
+
"types": "./jsonc.d.ts",
|
|
45
|
+
"import": "./jsonc.js",
|
|
46
|
+
"default": "./jsonc.js"
|
|
47
|
+
},
|
|
48
|
+
"./toml": {
|
|
49
|
+
"types": "./toml.d.ts",
|
|
50
|
+
"import": "./toml.js",
|
|
51
|
+
"default": "./toml.js"
|
|
52
|
+
},
|
|
53
|
+
"./yaml": {
|
|
54
|
+
"types": "./yaml.d.ts",
|
|
55
|
+
"import": "./yaml.js",
|
|
56
|
+
"default": "./yaml.js"
|
|
57
|
+
},
|
|
43
58
|
"./package.json": "./package.json"
|
|
44
59
|
},
|
|
45
60
|
"peerDependencies": {
|
|
46
61
|
"@effected/glob": "^0.14.0",
|
|
47
62
|
"@effected/jsonc": "^0.20.0",
|
|
48
63
|
"@effected/toml": "^0.16.0",
|
|
49
|
-
"@effected/walker": "^0.
|
|
50
|
-
"@effected/yaml": "^0.25.
|
|
64
|
+
"@effected/walker": "^0.20.0",
|
|
65
|
+
"@effected/yaml": "^0.25.1",
|
|
51
66
|
"effect": "^4.0.0"
|
|
52
67
|
},
|
|
68
|
+
"peerDependenciesMeta": {
|
|
69
|
+
"@effected/jsonc": {
|
|
70
|
+
"optional": true
|
|
71
|
+
},
|
|
72
|
+
"@effected/toml": {
|
|
73
|
+
"optional": true
|
|
74
|
+
},
|
|
75
|
+
"@effected/yaml": {
|
|
76
|
+
"optional": true
|
|
77
|
+
}
|
|
78
|
+
},
|
|
53
79
|
"engines": {
|
|
54
80
|
"node": ">=24.11.0"
|
|
55
81
|
}
|
package/toml.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ConfigCodec } from "@effected/config-file";
|
|
2
|
+
/**
|
|
3
|
+
* A `ConfigCodec` backed by `@effected/toml`.
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* `@effected/toml`'s input hardening — a nesting-depth cap on arrays and
|
|
7
|
+
* inline tables, enforced independently on both the parse and stringify
|
|
8
|
+
* sides — fails through the typed error channel, so a hostile config file
|
|
9
|
+
* surfaces as a `ConfigCodecError` rather than crashing the process. Both
|
|
10
|
+
* directions preserve the underlying failure structurally in `cause` —
|
|
11
|
+
* never stringified.
|
|
12
|
+
*
|
|
13
|
+
* Stringify is genuinely fallible beyond hostile input: TOML has no null,
|
|
14
|
+
* so a document carrying `null` (or any other unrepresentable value, an
|
|
15
|
+
* out-of-int64-range `bigint`, a circular reference) fails with a
|
|
16
|
+
* `ConfigCodecError` whose `cause` is the structured `TomlStringifyError`.
|
|
17
|
+
*
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
export declare const TomlCodec: ConfigCodec;
|
|
21
|
+
//# sourceMappingURL=toml.d.ts.map
|
package/toml.js
ADDED
package/yaml.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ConfigCodec } from "@effected/config-file";
|
|
2
|
+
/**
|
|
3
|
+
* A `ConfigCodec` backed by `@effected/yaml`.
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* `@effected/yaml`'s input hardening — an alias-expansion budget guarding
|
|
7
|
+
* against "billion laughs" alias bombs, and a collection-nesting depth cap —
|
|
8
|
+
* fails through the typed error channel, so a hostile config file surfaces
|
|
9
|
+
* as a `ConfigCodecError` rather than crashing the process. Both directions
|
|
10
|
+
* preserve the underlying failure structurally in `cause` — never
|
|
11
|
+
* stringified.
|
|
12
|
+
*
|
|
13
|
+
* @public
|
|
14
|
+
*/
|
|
15
|
+
export declare const YamlCodec: ConfigCodec;
|
|
16
|
+
//# sourceMappingURL=yaml.d.ts.map
|
package/yaml.js
ADDED