tyfigo 0.1.0-next.f085cb0.0 → 0.1.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 +184 -3
- package/dist/bun.d.mts +7 -0
- package/dist/bun.mjs +36 -0
- package/dist/bun.mjs.map +1 -0
- package/dist/effect.browser-Bhr-Yr7t.d.mts +61 -0
- package/dist/effect.browser-CO87TEE_.mjs +50 -0
- package/dist/effect.browser-CO87TEE_.mjs.map +1 -0
- package/dist/effect.browser.d.mts +5 -0
- package/dist/effect.browser.mjs +3 -0
- package/dist/effect.d.mts +6 -60
- package/dist/effect.mjs +7 -48
- package/dist/effect.mjs.map +1 -1
- package/dist/env-Bd-1Atoz.mjs +27 -0
- package/dist/env-Bd-1Atoz.mjs.map +1 -0
- package/dist/env-R3_XPW_u.mjs +78 -0
- package/dist/env-R3_XPW_u.mjs.map +1 -0
- package/dist/formats-D_dKXoPf.mjs +900 -0
- package/dist/formats-D_dKXoPf.mjs.map +1 -0
- package/dist/{resolve-BeFHJeZb.d.mts → index-jUM0zKD5.d.mts} +10 -52
- package/dist/index.browser-BUi5xv0g.d.mts +15 -0
- package/dist/index.browser-C5z_VT_3.mjs +17 -0
- package/dist/index.browser-C5z_VT_3.mjs.map +1 -0
- package/dist/index.browser.d.mts +5 -0
- package/dist/index.browser.mjs +3 -0
- package/dist/index.d.mts +5 -14
- package/dist/index.mjs +3 -12
- package/dist/index.mjs.map +1 -1
- package/dist/load-C-kweoyY.d.mts +53 -0
- package/dist/load-DxIjRpX6.mjs +127 -0
- package/dist/load-DxIjRpX6.mjs.map +1 -0
- package/dist/mapper.mjs +5 -2
- package/dist/mapper.mjs.map +1 -1
- package/dist/plugins/esbuild.d.mts +5 -0
- package/dist/plugins/esbuild.mjs +6 -0
- package/dist/plugins/esbuild.mjs.map +1 -0
- package/dist/plugins/jest.d.mts +10 -0
- package/dist/plugins/jest.mjs +29 -0
- package/dist/plugins/jest.mjs.map +1 -0
- package/dist/plugins/loader.d.mts +16 -0
- package/dist/plugins/loader.mjs +27 -0
- package/dist/plugins/loader.mjs.map +1 -0
- package/dist/plugins/next.d.mts +21 -0
- package/dist/plugins/next.mjs +126 -0
- package/dist/plugins/next.mjs.map +1 -0
- package/dist/plugins/rolldown.d.mts +5 -0
- package/dist/plugins/rolldown.mjs +6 -0
- package/dist/plugins/rolldown.mjs.map +1 -0
- package/dist/plugins/rollup.d.mts +5 -0
- package/dist/plugins/rollup.mjs +6 -0
- package/dist/plugins/rollup.mjs.map +1 -0
- package/dist/plugins/rsbuild.d.mts +5 -0
- package/dist/plugins/rsbuild.mjs +6 -0
- package/dist/plugins/rsbuild.mjs.map +1 -0
- package/dist/plugins/rspack.d.mts +5 -0
- package/dist/plugins/rspack.mjs +6 -0
- package/dist/plugins/rspack.mjs.map +1 -0
- package/dist/plugins/vite.d.mts +5 -0
- package/dist/plugins/vite.mjs +6 -0
- package/dist/plugins/vite.mjs.map +1 -0
- package/dist/plugins/webpack.d.mts +5 -0
- package/dist/plugins/webpack.mjs +6 -0
- package/dist/plugins/webpack.mjs.map +1 -0
- package/dist/positions-BCTEFZTX.mjs +89 -0
- package/dist/positions-BCTEFZTX.mjs.map +1 -0
- package/dist/problems-BNAqmOzL.mjs +23 -0
- package/dist/problems-BNAqmOzL.mjs.map +1 -0
- package/dist/register.d.mts +1 -0
- package/dist/register.mjs +26 -0
- package/dist/register.mjs.map +1 -0
- package/dist/resolve-D3geHCGs.mjs +242 -0
- package/dist/resolve-D3geHCGs.mjs.map +1 -0
- package/dist/runtime/index.d.mts +29 -35
- package/dist/runtime/index.mjs +4 -99
- package/dist/runtime-DeRUtPZ9.mjs +69 -0
- package/dist/runtime-DeRUtPZ9.mjs.map +1 -0
- package/dist/{schema-CNquZBC8.d.mts → schema-BsuU_Hup.d.mts} +2 -2
- package/dist/{schema-C31aywfc.mjs → schema-DcD4N9N-.mjs} +4 -24
- package/dist/schema-DcD4N9N-.mjs.map +1 -0
- package/dist/shared-BnK77Zcp.mjs +55 -0
- package/dist/shared-BnK77Zcp.mjs.map +1 -0
- package/dist/shared-CZ44uwQq.d.mts +23 -0
- package/dist/source-BWf_Y0YZ.d.mts +32 -0
- package/dist/source-CcI86kKk.mjs +9 -0
- package/dist/source-CcI86kKk.mjs.map +1 -0
- package/dist/sync-B-HAK-_E.mjs +14 -0
- package/dist/sync-B-HAK-_E.mjs.map +1 -0
- package/dist/unplugin-AC_HM_T2.d.mts +2 -0
- package/dist/unplugin-BQGm_bhA.mjs +83 -0
- package/dist/unplugin-BQGm_bhA.mjs.map +1 -0
- package/package.json +133 -12
- package/dist/placeholders-MqW2KO9Z.mjs +0 -511
- package/dist/placeholders-MqW2KO9Z.mjs.map +0 -1
- package/dist/resolve-CDWA58rz.mjs +0 -326
- package/dist/resolve-CDWA58rz.mjs.map +0 -1
- package/dist/runtime/index.mjs.map +0 -1
- package/dist/schema-C31aywfc.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -2,22 +2,203 @@
|
|
|
2
2
|
|
|
3
3
|
# tyfigo
|
|
4
4
|
|
|
5
|
-
**
|
|
5
|
+
**Typed, validated application config for TypeScript.**
|
|
6
6
|
|
|
7
7
|
[](https://github.com/imranbarbhuiya/tyfigo/blob/main/LICENSE)
|
|
8
8
|
[](https://www.npmjs.com/package/tyfigo)
|
|
9
9
|
|
|
10
10
|
</div>
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
`import config from './app.config.yaml'` gives you the config for the current environment: the base file merged with
|
|
13
|
+
`app.<env>.config.yaml` and your local overrides, `${PLACEHOLDERS}` filled in from `.env` files and the real environment, and
|
|
14
|
+
the result deep-frozen. The type comes from the file through a TypeScript 7.1 content mapper that runs inside `tsc` and the
|
|
15
|
+
editor, so there is no codegen step, and a wrong key or value in any layer is reported at its line in the config file. Add a
|
|
16
|
+
Standard Schema (Zod, Valibot, ArkType, …) or an Effect Schema when you need ranges, URLs, enums or required variables.
|
|
17
|
+
|
|
18
|
+
Read the documentation at [tyfigo.vercel.app](https://tyfigo.vercel.app).
|
|
14
19
|
|
|
15
20
|
## Install
|
|
16
21
|
|
|
22
|
+
tyfigo needs TypeScript 7.1, the first release with content mappers, and Node 24 or later (or Bun).
|
|
23
|
+
|
|
17
24
|
```bash
|
|
18
25
|
npm i tyfigo
|
|
26
|
+
npm i -D typescript@7.1.0-beta
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Register the content mapper
|
|
30
|
+
|
|
31
|
+
```jsonc
|
|
32
|
+
// tsconfig.json
|
|
33
|
+
{
|
|
34
|
+
"compilerOptions": {
|
|
35
|
+
"module": "nodenext",
|
|
36
|
+
"strict": true,
|
|
37
|
+
"noEmit": true,
|
|
38
|
+
"allowImportingTsExtensions": true,
|
|
39
|
+
"skipLibCheck": true,
|
|
40
|
+
"types": ["node"],
|
|
41
|
+
},
|
|
42
|
+
"contentMappers": [
|
|
43
|
+
{
|
|
44
|
+
"package": "tyfigo",
|
|
45
|
+
"extensions": [
|
|
46
|
+
".config.yaml",
|
|
47
|
+
".config.yml",
|
|
48
|
+
".config.toml",
|
|
49
|
+
".config.jsonc",
|
|
50
|
+
".config.json5",
|
|
51
|
+
".config.ini",
|
|
52
|
+
".config.properties",
|
|
53
|
+
".config.xml",
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
"include": ["src", "config"],
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`tsc` runs content mappers only with `--runExternalCode`:
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"scripts": {
|
|
66
|
+
"typecheck": "tsc --noEmit --runExternalCode"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Including `config` makes tsc check environment and local files even when no code imports them directly. TypeScript 7 defaults `types` to `[]`, so list `node` (or `bun`) yourself. `skipLibCheck` keeps tsc from checking the bundler types that the plugin declarations reference, which you only have installed for the bundler you use.
|
|
72
|
+
|
|
73
|
+
## Example
|
|
74
|
+
|
|
75
|
+
```yaml
|
|
76
|
+
# config/app.config.yaml
|
|
77
|
+
server:
|
|
78
|
+
host: localhost
|
|
79
|
+
port: ${PORT:-3000}
|
|
80
|
+
database:
|
|
81
|
+
url: ${DATABASE_URL}
|
|
82
|
+
pool:
|
|
83
|
+
max: 5
|
|
84
|
+
features: [search]
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
```yaml
|
|
88
|
+
# config/app.production.config.yaml
|
|
89
|
+
server:
|
|
90
|
+
port: 8080
|
|
91
|
+
hostname: 0.0.0.0
|
|
92
|
+
database:
|
|
93
|
+
pool:
|
|
94
|
+
max: twenty
|
|
95
|
+
features: [search, 42]
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
// src/main.ts
|
|
100
|
+
import config from '../config/app.config.yaml';
|
|
101
|
+
|
|
102
|
+
const port: string = config.server.port;
|
|
19
103
|
```
|
|
20
104
|
|
|
105
|
+
The type of `config` comes from the base file, widened so other environments can change values:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
// const config: {
|
|
109
|
+
// readonly server: { readonly host: string; readonly port: number };
|
|
110
|
+
// readonly database: { readonly url: string; readonly pool: { readonly max: number } };
|
|
111
|
+
// readonly features: readonly string[];
|
|
112
|
+
// }
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
and `tsc --noEmit --runExternalCode` reports each mistake at its line in the YAML or the code:
|
|
116
|
+
|
|
117
|
+
```txt
|
|
118
|
+
config/app.production.config.yaml(3,5): error TS2353: Object literal may only specify known properties, and 'hostname' does not exist in type '{ readonly host?: string | undefined; readonly port?: number | undefined; }'.
|
|
119
|
+
config/app.production.config.yaml(6,9): error TS2322: Type 'string' is not assignable to type 'number'.
|
|
120
|
+
config/app.production.config.yaml(7,20): error TS2322: Type 'number' is not assignable to type 'string'.
|
|
121
|
+
src/main.ts(3,7): error TS2322: Type 'number' is not assignable to type 'string'.
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`database.url` has no default, so set `DATABASE_URL` (in `.env` or the environment) before you run the example. Without it, the import fails
|
|
125
|
+
with `Environment variable 'DATABASE_URL' is not set (missing-variable)`.
|
|
126
|
+
|
|
127
|
+
Layers are read in this order, each one winning over the last: `app.config.yaml`, `app.<env>.config.yaml`,
|
|
128
|
+
`app.local.config.yaml`, `app.<env>.local.config.yaml`. The environment is `NODE_ENV` unless you pass `environment`.
|
|
129
|
+
Placeholders (`${VAR}`, `${VAR:-default}`, `${VAR:?message}`) read `.env`, `.env.local`, `.env.<env>` and `.env.<env>.local`,
|
|
130
|
+
with real environment variables on top. tyfigo never writes `process.env`.
|
|
131
|
+
|
|
132
|
+
## Schemas
|
|
133
|
+
|
|
134
|
+
`loadConfig` validates a config import and `loadEnv` validates environment variables, with any Standard Schema or an Effect
|
|
135
|
+
Schema. Errors list every issue with the `file:line:column` the value came from.
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
import { loadConfig, loadEnv } from 'tyfigo';
|
|
139
|
+
import { z } from 'zod';
|
|
140
|
+
|
|
141
|
+
import config from '../config/app.config.yaml';
|
|
142
|
+
|
|
143
|
+
const Config = z.object({
|
|
144
|
+
server: z.object({ host: z.string(), port: z.number().int().positive() }),
|
|
145
|
+
database: z.object({ url: z.url({ protocol: /^postgres$/ }) }),
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const settings = loadConfig(config, Config);
|
|
149
|
+
const env = loadEnv(z.object({ NODE_ENV: z.enum(['development', 'production', 'test']).default('development') }));
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
`loadConfigAsync` and `loadEnvAsync` take async schemas.
|
|
153
|
+
|
|
154
|
+
## Effect
|
|
155
|
+
|
|
156
|
+
`tyfigo/effect` has the same loaders returning Effects that fail with a typed `ConfigError`, plus `ConfigService`, layers and a
|
|
157
|
+
`ConfigProvider` that reads a config file. See [Effect](https://tyfigo.vercel.app/docs/effect).
|
|
158
|
+
|
|
159
|
+
## Runtimes and bundlers
|
|
160
|
+
|
|
161
|
+
| Target | Setup | Example |
|
|
162
|
+
| ---------------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
|
|
163
|
+
| Node 24+ | `node --import tyfigo/register src/main.ts` | [node](https://github.com/imranbarbhuiya/tyfigo/tree/main/examples/node) |
|
|
164
|
+
| Bun | `preload = ["tyfigo/bun"]` in `bunfig.toml`; `plugins: [tyfigo()]` from `tyfigo/bun` in `Bun.build` | [bun](https://github.com/imranbarbhuiya/tyfigo/tree/main/examples/bun) |
|
|
165
|
+
| nub | `"preload": ["tyfigo/register"]` in `nub.jsonc` | [nub](https://github.com/imranbarbhuiya/tyfigo/tree/main/examples/nub) |
|
|
166
|
+
| Vite and Vitest | `plugins: [tyfigo()]` from `tyfigo/vite` | [vite](https://github.com/imranbarbhuiya/tyfigo/tree/main/examples/vite) |
|
|
167
|
+
| Next.js (webpack, Turbopack) | `export default withTyfigo({})` from `tyfigo/next` | [next](https://github.com/imranbarbhuiya/tyfigo/tree/main/examples/next) |
|
|
168
|
+
| esbuild and tsup | `plugins: [tyfigo()]` (tsup: `esbuildPlugins`) from `tyfigo/esbuild` | [esbuild](https://github.com/imranbarbhuiya/tyfigo/tree/main/examples/esbuild) |
|
|
169
|
+
| Rollup | `plugins: [tyfigo({ target: 'server' })]` from `tyfigo/rollup` (defaults to browser) | |
|
|
170
|
+
| Rolldown and tsdown | `plugins: [tyfigo()]` from `tyfigo/rolldown` | |
|
|
171
|
+
| webpack | `plugins: [tyfigo()]` from `tyfigo/webpack` | [webpack](https://github.com/imranbarbhuiya/tyfigo/tree/main/examples/webpack) |
|
|
172
|
+
| Rspack and Rsbuild | `plugins: [tyfigo()]` from `tyfigo/rspack` or `tyfigo/rsbuild` | [rspack](https://github.com/imranbarbhuiya/tyfigo/tree/main/examples/rspack) |
|
|
173
|
+
| Jest | `'tyfigo/jest'` as the `transform` for config files, with their extensions in `moduleFileExtensions` | [jest](https://github.com/imranbarbhuiya/tyfigo/tree/main/examples/jest) |
|
|
174
|
+
|
|
175
|
+
Browser builds get the base and environment files inlined, and a `${VAR}` placeholder or an import of `loadEnv` in a client bundle
|
|
176
|
+
fails the build. Server bundles fix the environment when they build: they inline the base, environment and local files, ignore
|
|
177
|
+
`NODE_ENV` at run time for picking layers, and read `.env` files and environment variables when they start. See
|
|
178
|
+
[Browser builds](https://tyfigo.vercel.app/docs/browser-builds).
|
|
179
|
+
|
|
180
|
+
## Formats
|
|
181
|
+
|
|
182
|
+
tyfigo reads seven formats under eight suffixes, since YAML takes both `.config.yaml` and `.config.yml`.
|
|
183
|
+
|
|
184
|
+
| Suffix | Format |
|
|
185
|
+
| ----------------------------- | ------------- |
|
|
186
|
+
| `.config.yaml`, `.config.yml` | YAML |
|
|
187
|
+
| `.config.toml` | TOML |
|
|
188
|
+
| `.config.jsonc` | JSONC |
|
|
189
|
+
| `.config.json5` | JSON5 |
|
|
190
|
+
| `.config.ini` | INI |
|
|
191
|
+
| `.config.properties` | `.properties` |
|
|
192
|
+
| `.config.xml` | XML |
|
|
193
|
+
|
|
194
|
+
## tyfigo and Motherload
|
|
195
|
+
|
|
196
|
+
[Motherload](https://motherload.dev) also imports data files through TypeScript 7.1 content mappers, but it is a general data
|
|
197
|
+
loader: import any `.yaml`, `.toml`, `.csv`, `.md` or other data file and get its contents, typed as `as const` would type them. tyfigo only claims `*.config.*` files and is for the settings your app reads at start-up: widened
|
|
198
|
+
types, layered environment files checked against the base, placeholders and schema validation. They can share a project,
|
|
199
|
+
since TypeScript gives each extension one owner and the longest suffix wins, so tyfigo can own `.config.yaml` while
|
|
200
|
+
Motherload owns `.yaml`. See [tyfigo and Motherload](https://tyfigo.vercel.app/docs#tyfigo-and-motherload).
|
|
201
|
+
|
|
21
202
|
## Buy me some doughnuts
|
|
22
203
|
|
|
23
204
|
If you want to support me by donating, you can do so by using any of the following methods. Thank you very much in advance!
|
package/dist/bun.d.mts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { BunPlugin } from "bun";
|
|
2
|
+
/**
|
|
3
|
+
* The tyfigo Bun plugin. Importing `tyfigo/bun` (a `bunfig.toml` `preload`) registers it for the runtime and `bun test`; pass
|
|
4
|
+
* `tyfigo()` in `plugins` to use it with `Bun.build`, where a `browser` target inlines the config and a server target inlines the config files and reads `.env` files when it runs.
|
|
5
|
+
*/
|
|
6
|
+
export declare const tyfigo: () => BunPlugin;
|
|
7
|
+
//# sourceMappingURL=bun.d.mts.map
|
package/dist/bun.mjs
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { n as formatProblems } from "./problems-BNAqmOzL.mjs";
|
|
2
|
+
import { t as configSuffixes } from "./formats-D_dKXoPf.mjs";
|
|
3
|
+
import { t as load } from "./load-DxIjRpX6.mjs";
|
|
4
|
+
import { extname } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { plugin } from "bun";
|
|
7
|
+
// src/bun.ts
|
|
8
|
+
const runtime = fileURLToPath(new URL(`runtime/index${extname(import.meta.url)}`, import.meta.url));
|
|
9
|
+
const filter = new RegExp(`[^/\\\\](?:${configSuffixes.map((suffix) => suffix.replaceAll(".", "\\.")).join("|")})$`);
|
|
10
|
+
/**
|
|
11
|
+
* The tyfigo Bun plugin. Importing `tyfigo/bun` (a `bunfig.toml` `preload`) registers it for the runtime and `bun test`; pass
|
|
12
|
+
* `tyfigo()` in `plugins` to use it with `Bun.build`, where a `browser` target inlines the config and a server target inlines the config files and reads `.env` files when it runs.
|
|
13
|
+
*/
|
|
14
|
+
const tyfigo = () => ({
|
|
15
|
+
name: "tyfigo",
|
|
16
|
+
setup(build) {
|
|
17
|
+
const config = build.config;
|
|
18
|
+
const target = config?.target === "browser" ? "browser" : "server";
|
|
19
|
+
build.onLoad({ filter }, ({ path }) => {
|
|
20
|
+
const result = load(path, {
|
|
21
|
+
target,
|
|
22
|
+
runtime,
|
|
23
|
+
bundle: config !== void 0
|
|
24
|
+
});
|
|
25
|
+
if (result.problems.length > 0) throw new Error(`Invalid config ${path}\n${formatProblems(result.problems)}`);
|
|
26
|
+
return {
|
|
27
|
+
contents: result.code,
|
|
28
|
+
loader: "js"
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
plugin(tyfigo());
|
|
34
|
+
export { tyfigo };
|
|
35
|
+
|
|
36
|
+
//# sourceMappingURL=bun.mjs.map
|
package/dist/bun.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bun.mjs","names":[],"sources":["../src/bun.ts"],"sourcesContent":["import { extname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport { plugin } from 'bun';\n\nimport { configSuffixes, formatProblems } from './core/index.ts';\nimport { load } from './runtime/index.ts';\n\nimport type { BunPlugin } from 'bun';\n\n// The runtime next to this module, so it resolves from config files outside the dependency tree: `.ts` in this repo, `.mjs` once built.\nconst runtime = fileURLToPath(new URL(`runtime/index${extname(import.meta.url)}`, import.meta.url));\n\nconst filter = new RegExp(`[^/\\\\\\\\](?:${configSuffixes.map((suffix) => suffix.replaceAll('.', '\\\\.')).join('|')})$`);\n\n/**\n * The tyfigo Bun plugin. Importing `tyfigo/bun` (a `bunfig.toml` `preload`) registers it for the runtime and `bun test`; pass\n * `tyfigo()` in `plugins` to use it with `Bun.build`, where a `browser` target inlines the config and a server target inlines the config files and reads `.env` files when it runs.\n */\nexport const tyfigo = (): BunPlugin => ({\n\tname: 'tyfigo',\n\tsetup(build) {\n\t\tconst config = build.config as typeof build.config | undefined;\n\t\tconst target = config?.target === 'browser' ? 'browser' : 'server';\n\t\tbuild.onLoad({ filter }, ({ path }) => {\n\t\t\tconst result = load(path, { target, runtime, bundle: config !== undefined });\n\t\t\tif (result.problems.length > 0) {\n\t\t\t\tthrow new Error(`Invalid config ${path}\\n${formatProblems(result.problems)}`);\n\t\t\t}\n\t\t\treturn { contents: result.code, loader: 'js' };\n\t\t});\n\t},\n});\n\nvoid plugin(tyfigo());\n"],"mappings":";;;;;;;AAWA,MAAM,UAAU,cAAc,IAAI,IAAI,gBAAgB,QAAQ,YAAY,GAAG,KAAK,YAAY,GAAG,CAAC;AAElG,MAAM,SAAS,IAAI,OAAO,cAAc,eAAe,KAAK,WAAW,OAAO,WAAW,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,GAAG;;;;;AAMnH,MAAa,gBAA2B;CACvC,MAAM;CACN,MAAM,OAAO;EACZ,MAAM,SAAS,MAAM;EACrB,MAAM,SAAS,QAAQ,WAAW,YAAY,YAAY;EAC1D,MAAM,OAAO,EAAE,OAAO,IAAI,EAAE,WAAW;GACtC,MAAM,SAAS,KAAK,MAAM;IAAE;IAAQ;IAAS,QAAQ,WAAW,KAAA;GAAU,CAAC;GAC3E,IAAI,OAAO,SAAS,SAAS,GAC5B,MAAM,IAAI,MAAM,kBAAkB,KAAK,IAAI,eAAe,OAAO,QAAQ,GAAG;GAE7E,OAAO;IAAE,UAAU,OAAO;IAAM,QAAQ;GAAK;EAC9C,CAAC;CACF;AACD;AAEK,OAAO,OAAO,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import "./index-jUM0zKD5.mjs";
|
|
2
|
+
import { a as LooseInput, o as SchemaInput, r as ConfigValidationError, s as SchemaOutput } from "./schema-BsuU_Hup.mjs";
|
|
3
|
+
import { ConfigProvider, Context, Effect, Layer, Schema } from "effect";
|
|
4
|
+
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
5
|
+
/**
|
|
6
|
+
* A Standard Schema or any Effect Schema, including one that needs services to decode.
|
|
7
|
+
*/
|
|
8
|
+
type EffectConfigSchema = StandardSchemaV1 | Schema.Top;
|
|
9
|
+
/**
|
|
10
|
+
* The services an Effect Schema needs to decode, or `never` for a Standard Schema.
|
|
11
|
+
*/
|
|
12
|
+
type SchemaServices<S> = S extends StandardSchemaV1 ? never : S extends Schema.Top ? S['DecodingServices'] : never;
|
|
13
|
+
/**
|
|
14
|
+
* The error of every tyfigo Effect: a config or the environment does not match its schema, or a config file could not
|
|
15
|
+
* be resolved. It is the same class the Promise API throws, tagged `ConfigValidationError`.
|
|
16
|
+
*/
|
|
17
|
+
declare const ConfigError: typeof ConfigValidationError;
|
|
18
|
+
type ConfigError = ConfigValidationError;
|
|
19
|
+
/**
|
|
20
|
+
* Validates a config import (or any value) with a Standard Schema or an Effect Schema. Effect Schemas are decoded by
|
|
21
|
+
* Effect itself, so schemas that need services or decode asynchronously work, and so do async Standard Schemas.
|
|
22
|
+
*/
|
|
23
|
+
declare const loadConfig: <S extends EffectConfigSchema>(config: LooseInput<SchemaInput<S>>, schema: S) => Effect.Effect<SchemaOutput<S>, ConfigError, SchemaServices<S>>;
|
|
24
|
+
/**
|
|
25
|
+
* A layer that provides `service` with the validated config.
|
|
26
|
+
*/
|
|
27
|
+
declare const layerConfig: <I, S extends EffectConfigSchema>(service: Context.Key<I, SchemaOutput<S>>, config: LooseInput<SchemaInput<S>>, schema: S) => Layer.Layer<I, ConfigError, SchemaServices<S>>;
|
|
28
|
+
/**
|
|
29
|
+
* Defines a service class for a validated config, with a `layer` that provides it.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* class AppConfig extends ConfigService<AppConfig>()('app/AppConfig', config, schema) {}
|
|
34
|
+
*
|
|
35
|
+
* const program = Effect.gen(function* () {
|
|
36
|
+
* const { port } = yield* AppConfig;
|
|
37
|
+
* }).pipe(Effect.provide(AppConfig.layer));
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
declare const ConfigService: <Self>() => <const Identifier extends string, S extends EffectConfigSchema>(id: Identifier, config: LooseInput<SchemaInput<S>>, schema: S) => Context.ServiceClass<Self, Identifier, SchemaOutput<S>> & {
|
|
41
|
+
layer: Layer.Layer<Self, ConfigValidationError, Exclude<SchemaServices<S>, import("effect/Scope").Scope>>;
|
|
42
|
+
};
|
|
43
|
+
interface ConfigProviderLayerOptions {
|
|
44
|
+
/**
|
|
45
|
+
* Whether the config files win over the provider already installed (by default the environment variables). Defaults to
|
|
46
|
+
* `true`; with `false` the config files only fill in paths the current provider does not have.
|
|
47
|
+
*/
|
|
48
|
+
readonly asPrimary?: boolean | undefined;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* A `ConfigProvider` that reads a config import (or any object): nested keys are path segments and array items are
|
|
52
|
+
* numeric segments, so `Config.Number('port').pipe(Config.nested('server'))` reads `server.port`. Empty strings are kept.
|
|
53
|
+
*/
|
|
54
|
+
declare const configProvider: (config: unknown) => ConfigProvider.ConfigProvider;
|
|
55
|
+
/**
|
|
56
|
+
* Installs {@link configProvider} for a config import on top of the current `ConfigProvider`, which falls back to the
|
|
57
|
+
* environment variables for paths the config does not have.
|
|
58
|
+
*/
|
|
59
|
+
declare const layerConfigProvider: (config: unknown, options?: ConfigProviderLayerOptions) => Layer.Layer<never>;
|
|
60
|
+
export { ConfigService as a, layerConfig as c, ConfigError as i, loadConfig as l, configProvider as n, EffectConfigSchema as o, layerConfigProvider as r, SchemaServices as s, ConfigProviderLayerOptions as t };
|
|
61
|
+
//# sourceMappingURL=effect.browser-Bhr-Yr7t.d.mts.map
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { n as decodeConfig, t as ConfigValidationError } from "./schema-DcD4N9N-.mjs";
|
|
2
|
+
import { ConfigProvider, Context, Layer } from "effect";
|
|
3
|
+
// src/effect/load.ts
|
|
4
|
+
/**
|
|
5
|
+
* The error of every tyfigo Effect: a config or the environment does not match its schema, or a config file could not
|
|
6
|
+
* be resolved. It is the same class the Promise API throws, tagged `ConfigValidationError`.
|
|
7
|
+
*/
|
|
8
|
+
const ConfigError = ConfigValidationError;
|
|
9
|
+
/**
|
|
10
|
+
* Validates a config import (or any value) with a Standard Schema or an Effect Schema. Effect Schemas are decoded by
|
|
11
|
+
* Effect itself, so schemas that need services or decode asynchronously work, and so do async Standard Schemas.
|
|
12
|
+
*/
|
|
13
|
+
const loadConfig = (config, schema) => decodeConfig(config, schema, {
|
|
14
|
+
async: true,
|
|
15
|
+
native: true
|
|
16
|
+
});
|
|
17
|
+
/**
|
|
18
|
+
* A layer that provides `service` with the validated config.
|
|
19
|
+
*/
|
|
20
|
+
const layerConfig = (service, config, schema) => Layer.effect(service, loadConfig(config, schema));
|
|
21
|
+
/**
|
|
22
|
+
* Defines a service class for a validated config, with a `layer` that provides it.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* class AppConfig extends ConfigService<AppConfig>()('app/AppConfig', config, schema) {}
|
|
27
|
+
*
|
|
28
|
+
* const program = Effect.gen(function* () {
|
|
29
|
+
* const { port } = yield* AppConfig;
|
|
30
|
+
* }).pipe(Effect.provide(AppConfig.layer));
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
const ConfigService = () => (id, config, schema) => {
|
|
34
|
+
const service = Context.Service()(id);
|
|
35
|
+
return Object.assign(service, { layer: Layer.effect(service, loadConfig(config, schema)) });
|
|
36
|
+
};
|
|
37
|
+
// src/effect/provider.ts
|
|
38
|
+
/**
|
|
39
|
+
* A `ConfigProvider` that reads a config import (or any object): nested keys are path segments and array items are
|
|
40
|
+
* numeric segments, so `Config.Number('port').pipe(Config.nested('server'))` reads `server.port`. Empty strings are kept.
|
|
41
|
+
*/
|
|
42
|
+
const configProvider = (config) => ConfigProvider.fromUnknown(config, { preserveEmptyStrings: true });
|
|
43
|
+
/**
|
|
44
|
+
* Installs {@link configProvider} for a config import on top of the current `ConfigProvider`, which falls back to the
|
|
45
|
+
* environment variables for paths the config does not have.
|
|
46
|
+
*/
|
|
47
|
+
const layerConfigProvider = (config, options = {}) => ConfigProvider.layerAdd(configProvider(config), { asPrimary: options.asPrimary ?? true });
|
|
48
|
+
export { layerConfig as a, ConfigService as i, layerConfigProvider as n, loadConfig as o, ConfigError as r, configProvider as t };
|
|
49
|
+
|
|
50
|
+
//# sourceMappingURL=effect.browser-CO87TEE_.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"effect.browser-CO87TEE_.mjs","names":[],"sources":["../src/effect/load.ts","../src/effect/provider.ts"],"sourcesContent":["import { Context, Layer } from 'effect';\n\nimport { ConfigValidationError, decodeConfig } from '../schema.ts';\n\nimport type { ConfigSchema, LooseInput, SchemaInput, SchemaOutput } from '../schema.ts';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport type { Effect, Schema } from 'effect';\n\n/**\n * A Standard Schema or any Effect Schema, including one that needs services to decode.\n */\nexport type EffectConfigSchema = StandardSchemaV1 | Schema.Top;\n\n/**\n * The services an Effect Schema needs to decode, or `never` for a Standard Schema.\n */\nexport type SchemaServices<S> = S extends StandardSchemaV1\n\t? never\n\t: S extends Schema.Top\n\t\t? S['DecodingServices']\n\t\t: never;\n\n/**\n * The error of every tyfigo Effect: a config or the environment does not match its schema, or a config file could not\n * be resolved. It is the same class the Promise API throws, tagged `ConfigValidationError`.\n */\nexport const ConfigError = ConfigValidationError;\nexport type ConfigError = ConfigValidationError;\n\n/**\n * Validates a config import (or any value) with a Standard Schema or an Effect Schema. Effect Schemas are decoded by\n * Effect itself, so schemas that need services or decode asynchronously work, and so do async Standard Schemas.\n */\nexport const loadConfig = <S extends EffectConfigSchema>(\n\tconfig: LooseInput<SchemaInput<S>>,\n\tschema: S,\n): Effect.Effect<SchemaOutput<S>, ConfigError, SchemaServices<S>> =>\n\tdecodeConfig(config, schema as ConfigSchema, { async: true, native: true }) as Effect.Effect<\n\t\tSchemaOutput<S>,\n\t\tConfigError,\n\t\tSchemaServices<S>\n\t>;\n\n/**\n * A layer that provides `service` with the validated config.\n */\nexport const layerConfig = <I, S extends EffectConfigSchema>(\n\tservice: Context.Key<I, SchemaOutput<S>>,\n\tconfig: LooseInput<SchemaInput<S>>,\n\tschema: S,\n): Layer.Layer<I, ConfigError, SchemaServices<S>> => Layer.effect(service, loadConfig(config, schema));\n\n/**\n * Defines a service class for a validated config, with a `layer` that provides it.\n *\n * @example\n * ```ts\n * class AppConfig extends ConfigService<AppConfig>()('app/AppConfig', config, schema) {}\n *\n * const program = Effect.gen(function* () {\n * \tconst { port } = yield* AppConfig;\n * }).pipe(Effect.provide(AppConfig.layer));\n * ```\n */\nexport const ConfigService =\n\t<Self>() =>\n\t<const Identifier extends string, S extends EffectConfigSchema>(\n\t\tid: Identifier,\n\t\tconfig: LooseInput<SchemaInput<S>>,\n\t\tschema: S,\n\t) => {\n\t\tconst service = Context.Service<Self, SchemaOutput<S>>()(id);\n\t\treturn Object.assign(service, {\n\t\t\tlayer: Layer.effect(service, loadConfig(config, schema)),\n\t\t});\n\t};\n","import { ConfigProvider } from 'effect';\n\nimport type { Layer } from 'effect';\n\nexport interface ConfigProviderLayerOptions {\n\t/**\n\t * Whether the config files win over the provider already installed (by default the environment variables). Defaults to\n\t * `true`; with `false` the config files only fill in paths the current provider does not have.\n\t */\n\treadonly asPrimary?: boolean | undefined;\n}\n\n/**\n * A `ConfigProvider` that reads a config import (or any object): nested keys are path segments and array items are\n * numeric segments, so `Config.Number('port').pipe(Config.nested('server'))` reads `server.port`. Empty strings are kept.\n */\nexport const configProvider = (config: unknown): ConfigProvider.ConfigProvider =>\n\tConfigProvider.fromUnknown(config, { preserveEmptyStrings: true });\n\n/**\n * Installs {@link configProvider} for a config import on top of the current `ConfigProvider`, which falls back to the\n * environment variables for paths the config does not have.\n */\nexport const layerConfigProvider = (config: unknown, options: ConfigProviderLayerOptions = {}): Layer.Layer<never> =>\n\tConfigProvider.layerAdd(configProvider(config), { asPrimary: options.asPrimary ?? true });\n"],"mappings":";;;;;;;AA0BA,MAAa,cAAc;;;;;AAO3B,MAAa,cACZ,QACA,WAEA,aAAa,QAAQ,QAAwB;CAAE,OAAO;CAAM,QAAQ;AAAK,CAAC;;;;AAS3E,MAAa,eACZ,SACA,QACA,WACoD,MAAM,OAAO,SAAS,WAAW,QAAQ,MAAM,CAAC;;;;;;;;;;;;;AAcrG,MAAa,uBAGX,IACA,QACA,WACI;CACJ,MAAM,UAAU,QAAQ,QAA+B,CAAC,CAAC,EAAE;CAC3D,OAAO,OAAO,OAAO,SAAS,EAC7B,OAAO,MAAM,OAAO,SAAS,WAAW,QAAQ,MAAM,CAAC,EACxD,CAAC;AACF;;;;;;AC3DD,MAAa,kBAAkB,WAC9B,eAAe,YAAY,QAAQ,EAAE,sBAAsB,KAAK,CAAC;;;;;AAMlE,MAAa,uBAAuB,QAAiB,UAAsC,CAAC,MAC3F,eAAe,SAAS,eAAe,MAAM,GAAG,EAAE,WAAW,QAAQ,aAAa,KAAK,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { a as LocatedSpan, c as Path, i as ResolvedLayer, l as Span, n as ResolveOptions, o as Problem, r as ResolvedConfig, s as ProblemCode } from "./index-jUM0zKD5.mjs";
|
|
2
|
+
import { t as ConfigSource } from "./source-BWf_Y0YZ.mjs";
|
|
3
|
+
import { a as LooseInput, i as LoadEnvOptions, n as ConfigSchema, o as SchemaInput, r as ConfigValidationError, s as SchemaOutput, t as ConfigIssue } from "./schema-BsuU_Hup.mjs";
|
|
4
|
+
import { a as ConfigService, c as layerConfig, i as ConfigError, l as loadConfig, n as configProvider, o as EffectConfigSchema, r as layerConfigProvider, s as SchemaServices, t as ConfigProviderLayerOptions } from "./effect.browser-Bhr-Yr7t.mjs";
|
|
5
|
+
export { ConfigError, type ConfigIssue, ConfigProviderLayerOptions, type ConfigSchema, ConfigService, type ConfigSource, ConfigValidationError, EffectConfigSchema, type LoadEnvOptions, type LocatedSpan, type LooseInput, type Path, type Problem, type ProblemCode, type ResolveOptions, type ResolvedConfig, type ResolvedLayer, type SchemaInput, type SchemaOutput, SchemaServices, type Span, configProvider, layerConfig, layerConfigProvider, loadConfig };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { t as ConfigValidationError } from "./schema-DcD4N9N-.mjs";
|
|
2
|
+
import { a as layerConfig, i as ConfigService, n as layerConfigProvider, o as loadConfig, r as ConfigError, t as configProvider } from "./effect.browser-CO87TEE_.mjs";
|
|
3
|
+
export { ConfigError, ConfigService, ConfigValidationError, configProvider, layerConfig, layerConfigProvider, loadConfig };
|
package/dist/effect.d.mts
CHANGED
|
@@ -1,79 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
* A Standard Schema or any Effect Schema, including one that needs services to decode.
|
|
7
|
-
*/
|
|
8
|
-
export type EffectConfigSchema = StandardSchemaV1 | Schema.Top;
|
|
9
|
-
/**
|
|
10
|
-
* The services an Effect Schema needs to decode, or `never` for a Standard Schema.
|
|
11
|
-
*/
|
|
12
|
-
export type SchemaServices<S> = S extends StandardSchemaV1 ? never : S extends Schema.Top ? S['DecodingServices'] : never;
|
|
13
|
-
/**
|
|
14
|
-
* The error of every tyfigo Effect: a config or the environment does not match its schema, or a config file could not
|
|
15
|
-
* be resolved. It is the same class the Promise API throws, tagged `ConfigValidationError`.
|
|
16
|
-
*/
|
|
17
|
-
export declare const ConfigError: typeof ConfigValidationError;
|
|
18
|
-
export type ConfigError = ConfigValidationError;
|
|
19
|
-
/**
|
|
20
|
-
* Validates a config import (or any value) with a Standard Schema or an Effect Schema. Effect Schemas are decoded by
|
|
21
|
-
* Effect itself, so schemas that need services or decode asynchronously work, and so do async Standard Schemas.
|
|
22
|
-
*/
|
|
23
|
-
export declare const loadConfig: <S extends EffectConfigSchema>(config: LooseInput<SchemaInput<S>>, schema: S) => Effect.Effect<SchemaOutput<S>, ConfigError, SchemaServices<S>>;
|
|
1
|
+
import { a as LocatedSpan, c as Path, i as ResolvedLayer, l as Span, n as ResolveOptions, o as Problem, r as ResolvedConfig, s as ProblemCode } from "./index-jUM0zKD5.mjs";
|
|
2
|
+
import { t as ConfigSource } from "./source-BWf_Y0YZ.mjs";
|
|
3
|
+
import { a as LooseInput, i as LoadEnvOptions, n as ConfigSchema, o as SchemaInput, r as ConfigValidationError, s as SchemaOutput, t as ConfigIssue } from "./schema-BsuU_Hup.mjs";
|
|
4
|
+
import { a as ConfigService, c as layerConfig, i as ConfigError, l as loadConfig, n as configProvider, o as EffectConfigSchema, r as layerConfigProvider, s as SchemaServices, t as ConfigProviderLayerOptions } from "./effect.browser-Bhr-Yr7t.mjs";
|
|
5
|
+
import { ConfigProvider, Context, Effect, Layer } from "effect";
|
|
24
6
|
/**
|
|
25
7
|
* Validates `.env`, `.env.local`, `.env.<environment>` and `.env.<environment>.local` with the real environment on top.
|
|
26
8
|
* Never writes `process.env`.
|
|
27
9
|
*/
|
|
28
10
|
export declare const loadEnv: <S extends EffectConfigSchema>(schema: S, options?: LoadEnvOptions) => Effect.Effect<SchemaOutput<S>, ConfigError, SchemaServices<S>>;
|
|
29
|
-
/**
|
|
30
|
-
* A layer that provides `service` with the validated config.
|
|
31
|
-
*/
|
|
32
|
-
export declare const layerConfig: <I, S extends EffectConfigSchema>(service: Context.Key<I, SchemaOutput<S>>, config: LooseInput<SchemaInput<S>>, schema: S) => Layer.Layer<I, ConfigError, SchemaServices<S>>;
|
|
33
11
|
/**
|
|
34
12
|
* A layer that provides `service` with the validated environment variables.
|
|
35
13
|
*/
|
|
36
14
|
export declare const layerEnv: <I, S extends EffectConfigSchema>(service: Context.Key<I, SchemaOutput<S>>, schema: S, options?: LoadEnvOptions) => Layer.Layer<I, ConfigError, SchemaServices<S>>;
|
|
37
|
-
/**
|
|
38
|
-
* Defines a service class for a validated config, with a `layer` that provides it.
|
|
39
|
-
*
|
|
40
|
-
* @example
|
|
41
|
-
* ```ts
|
|
42
|
-
* class AppConfig extends ConfigService<AppConfig>()('app/AppConfig', config, schema) {}
|
|
43
|
-
*
|
|
44
|
-
* const program = Effect.gen(function* () {
|
|
45
|
-
* const { port } = yield* AppConfig;
|
|
46
|
-
* }).pipe(Effect.provide(AppConfig.layer));
|
|
47
|
-
* ```
|
|
48
|
-
*/
|
|
49
|
-
export declare const ConfigService: <Self>() => <const Identifier extends string, S extends EffectConfigSchema>(id: Identifier, config: LooseInput<SchemaInput<S>>, schema: S) => Context.ServiceClass<Self, Identifier, SchemaOutput<S>> & {
|
|
50
|
-
layer: Layer.Layer<Self, ConfigValidationError, Exclude<SchemaServices<S>, import("effect/Scope").Scope>>;
|
|
51
|
-
};
|
|
52
|
-
export interface ConfigProviderLayerOptions {
|
|
53
|
-
/**
|
|
54
|
-
* Whether the config files win over the provider already installed (by default the environment variables). Defaults to
|
|
55
|
-
* `true`; with `false` the config files only fill in paths the current provider does not have.
|
|
56
|
-
*/
|
|
57
|
-
readonly asPrimary?: boolean | undefined;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* A `ConfigProvider` that reads a config import (or any object): nested keys are path segments and array items are
|
|
61
|
-
* numeric segments, so `Config.Number('port').pipe(Config.nested('server'))` reads `server.port`. Empty strings are kept.
|
|
62
|
-
*/
|
|
63
|
-
export declare const configProvider: (config: unknown) => ConfigProvider.ConfigProvider;
|
|
64
15
|
/**
|
|
65
16
|
* Resolves a config file with its layers, `.env` files and placeholders, like a config import does, and returns a
|
|
66
17
|
* `ConfigProvider` over the merged value.
|
|
67
18
|
*/
|
|
68
19
|
export declare const configProviderFromFile: (file: string, options?: ResolveOptions | undefined) => Effect.Effect<ConfigProvider.ConfigProvider, ConfigValidationError, never>;
|
|
69
|
-
/**
|
|
70
|
-
* Installs {@link configProvider} for a config import on top of the current `ConfigProvider`, which falls back to the
|
|
71
|
-
* environment variables for paths the config does not have.
|
|
72
|
-
*/
|
|
73
|
-
export declare const layerConfigProvider: (config: unknown, options?: ConfigProviderLayerOptions) => Layer.Layer<never>;
|
|
74
20
|
/**
|
|
75
21
|
* Installs {@link configProviderFromFile} on top of the current `ConfigProvider`.
|
|
76
22
|
*/
|
|
77
23
|
export declare const layerConfigProviderFromFile: (file: string, options?: ResolveOptions & ConfigProviderLayerOptions) => Layer.Layer<never, ConfigError>;
|
|
78
|
-
export { type ConfigIssue, type ConfigSchema, type ConfigSource, ConfigValidationError, type LoadEnvOptions, type LocatedSpan, type LooseInput, type Path, type Problem, type ProblemCode, type ResolveOptions, type ResolvedConfig, type ResolvedLayer, type SchemaInput, type SchemaOutput, type Span };
|
|
24
|
+
export { ConfigError, type ConfigIssue, ConfigProviderLayerOptions, type ConfigSchema, ConfigService, type ConfigSource, ConfigValidationError, EffectConfigSchema, type LoadEnvOptions, type LocatedSpan, type LooseInput, type Path, type Problem, type ProblemCode, type ResolveOptions, type ResolvedConfig, type ResolvedLayer, type SchemaInput, type SchemaOutput, SchemaServices, type Span, configProvider, layerConfig, layerConfigProvider, loadConfig };
|
|
79
25
|
//# sourceMappingURL=effect.d.mts.map
|
package/dist/effect.mjs
CHANGED
|
@@ -1,20 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* be resolved. It is the same class the Promise API throws, tagged `ConfigValidationError`.
|
|
8
|
-
*/
|
|
9
|
-
const ConfigError = ConfigValidationError;
|
|
10
|
-
/**
|
|
11
|
-
* Validates a config import (or any value) with a Standard Schema or an Effect Schema. Effect Schemas are decoded by
|
|
12
|
-
* Effect itself, so schemas that need services or decode asynchronously work, and so do async Standard Schemas.
|
|
13
|
-
*/
|
|
14
|
-
const loadConfig = (config, schema) => decodeConfig(config, schema, {
|
|
15
|
-
async: true,
|
|
16
|
-
native: true
|
|
17
|
-
});
|
|
1
|
+
import { t as ConfigValidationError } from "./schema-DcD4N9N-.mjs";
|
|
2
|
+
import { t as decodeEnv } from "./env-Bd-1Atoz.mjs";
|
|
3
|
+
import { a as layerConfig, i as ConfigService, n as layerConfigProvider, o as loadConfig, r as ConfigError, t as configProvider } from "./effect.browser-CO87TEE_.mjs";
|
|
4
|
+
import { t as resolveConfig } from "./resolve-D3geHCGs.mjs";
|
|
5
|
+
import { ConfigProvider, Effect, Layer } from "effect";
|
|
6
|
+
// src/effect/env.ts
|
|
18
7
|
/**
|
|
19
8
|
* Validates `.env`, `.env.local`, `.env.<environment>` and `.env.<environment>.local` with the real environment on top.
|
|
20
9
|
* Never writes `process.env`.
|
|
@@ -25,35 +14,10 @@ const loadEnv = (schema, options = {}) => decodeEnv(schema, {
|
|
|
25
14
|
native: true
|
|
26
15
|
});
|
|
27
16
|
/**
|
|
28
|
-
* A layer that provides `service` with the validated config.
|
|
29
|
-
*/
|
|
30
|
-
const layerConfig = (service, config, schema) => Layer.effect(service, loadConfig(config, schema));
|
|
31
|
-
/**
|
|
32
17
|
* A layer that provides `service` with the validated environment variables.
|
|
33
18
|
*/
|
|
34
19
|
const layerEnv = (service, schema, options = {}) => Layer.effect(service, loadEnv(schema, options));
|
|
35
|
-
|
|
36
|
-
* Defines a service class for a validated config, with a `layer` that provides it.
|
|
37
|
-
*
|
|
38
|
-
* @example
|
|
39
|
-
* ```ts
|
|
40
|
-
* class AppConfig extends ConfigService<AppConfig>()('app/AppConfig', config, schema) {}
|
|
41
|
-
*
|
|
42
|
-
* const program = Effect.gen(function* () {
|
|
43
|
-
* const { port } = yield* AppConfig;
|
|
44
|
-
* }).pipe(Effect.provide(AppConfig.layer));
|
|
45
|
-
* ```
|
|
46
|
-
*/
|
|
47
|
-
const ConfigService = () => (id, config, schema) => {
|
|
48
|
-
const service = Context.Service()(id);
|
|
49
|
-
return Object.assign(service, { layer: Layer.effect(service, loadConfig(config, schema)) });
|
|
50
|
-
};
|
|
51
|
-
// src/effect/provider.ts
|
|
52
|
-
/**
|
|
53
|
-
* A `ConfigProvider` that reads a config import (or any object): nested keys are path segments and array items are
|
|
54
|
-
* numeric segments, so `Config.Number('port').pipe(Config.nested('server'))` reads `server.port`. Empty strings are kept.
|
|
55
|
-
*/
|
|
56
|
-
const configProvider = (config) => ConfigProvider.fromUnknown(config, { preserveEmptyStrings: true });
|
|
20
|
+
// src/effect/file-provider.ts
|
|
57
21
|
/**
|
|
58
22
|
* Resolves a config file with its layers, `.env` files and placeholders, like a config import does, and returns a
|
|
59
23
|
* `ConfigProvider` over the merged value.
|
|
@@ -71,11 +35,6 @@ const configProviderFromFile = Effect.fn("configProviderFromFile")(function* (fi
|
|
|
71
35
|
return configProvider(result.value);
|
|
72
36
|
});
|
|
73
37
|
/**
|
|
74
|
-
* Installs {@link configProvider} for a config import on top of the current `ConfigProvider`, which falls back to the
|
|
75
|
-
* environment variables for paths the config does not have.
|
|
76
|
-
*/
|
|
77
|
-
const layerConfigProvider = (config, options = {}) => ConfigProvider.layerAdd(configProvider(config), { asPrimary: options.asPrimary ?? true });
|
|
78
|
-
/**
|
|
79
38
|
* Installs {@link configProviderFromFile} on top of the current `ConfigProvider`.
|
|
80
39
|
*/
|
|
81
40
|
const layerConfigProviderFromFile = (file, options = {}) => ConfigProvider.layerAdd(configProviderFromFile(file, options), { asPrimary: options.asPrimary ?? true });
|
package/dist/effect.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"effect.mjs","names":[],"sources":["../src/effect/
|
|
1
|
+
{"version":3,"file":"effect.mjs","names":[],"sources":["../src/effect/env.ts","../src/effect/file-provider.ts"],"sourcesContent":["import { Layer } from 'effect';\n\nimport { decodeEnv } from '../env.ts';\n\nimport type { ConfigSchema, LoadEnvOptions, SchemaOutput } from '../schema.ts';\nimport type { ConfigError, EffectConfigSchema, SchemaServices } from './load.ts';\nimport type { Context, Effect } from 'effect';\n\n/**\n * Validates `.env`, `.env.local`, `.env.<environment>` and `.env.<environment>.local` with the real environment on top.\n * Never writes `process.env`.\n */\nexport const loadEnv = <S extends EffectConfigSchema>(\n\tschema: S,\n\toptions: LoadEnvOptions = {},\n): Effect.Effect<SchemaOutput<S>, ConfigError, SchemaServices<S>> =>\n\tdecodeEnv(schema as ConfigSchema, { ...options, async: true, native: true }) as Effect.Effect<\n\t\tSchemaOutput<S>,\n\t\tConfigError,\n\t\tSchemaServices<S>\n\t>;\n\n/**\n * A layer that provides `service` with the validated environment variables.\n */\nexport const layerEnv = <I, S extends EffectConfigSchema>(\n\tservice: Context.Key<I, SchemaOutput<S>>,\n\tschema: S,\n\toptions: LoadEnvOptions = {},\n): Layer.Layer<I, ConfigError, SchemaServices<S>> => Layer.effect(service, loadEnv(schema, options));\n","import { ConfigProvider, Effect } from 'effect';\n\nimport { configProvider } from './provider.ts';\n\nimport { resolveConfig } from '../core/resolve.ts';\nimport { ConfigValidationError } from '../schema.ts';\n\nimport type { ResolveOptions } from '../core/resolve.ts';\nimport type { ConfigError } from './load.ts';\nimport type { ConfigProviderLayerOptions } from './provider.ts';\nimport type { Layer } from 'effect';\n\n/**\n * Resolves a config file with its layers, `.env` files and placeholders, like a config import does, and returns a\n * `ConfigProvider` over the merged value.\n */\nexport const configProviderFromFile = Effect.fn('configProviderFromFile')(function* (\n\tfile: string,\n\toptions: ResolveOptions = {},\n): Effect.fn.Return<ConfigProvider.ConfigProvider, ConfigError> {\n\tconst result = yield* resolveConfig(file, options);\n\tif (result.problems.length > 0) {\n\t\treturn yield* new ConfigValidationError({\n\t\t\tsubject: `config ${result.file}`,\n\t\t\tissues: result.problems.map((problem) => ({\n\t\t\t\tpath: [],\n\t\t\t\tmessage: `${problem.message} (${problem.code})`,\n\t\t\t\tlocation: `${problem.file}:${problem.line}:${problem.column}`,\n\t\t\t})),\n\t\t});\n\t}\n\treturn configProvider(result.value);\n});\n\n/**\n * Installs {@link configProviderFromFile} on top of the current `ConfigProvider`.\n */\nexport const layerConfigProviderFromFile = (\n\tfile: string,\n\toptions: ResolveOptions & ConfigProviderLayerOptions = {},\n): Layer.Layer<never, ConfigError> =>\n\tConfigProvider.layerAdd(configProviderFromFile(file, options), { asPrimary: options.asPrimary ?? true });\n"],"mappings":";;;;;;;;;;AAYA,MAAa,WACZ,QACA,UAA0B,CAAC,MAE3B,UAAU,QAAwB;CAAE,GAAG;CAAS,OAAO;CAAM,QAAQ;AAAK,CAAC;;;;AAS5E,MAAa,YACZ,SACA,QACA,UAA0B,CAAC,MACyB,MAAM,OAAO,SAAS,QAAQ,QAAQ,OAAO,CAAC;;;;;;ACbnG,MAAa,yBAAyB,OAAO,GAAG,wBAAwB,CAAC,CAAC,WACzE,MACA,UAA0B,CAAC,GACoC;CAC/D,MAAM,SAAS,OAAO,cAAc,MAAM,OAAO;CACjD,IAAI,OAAO,SAAS,SAAS,GAC5B,OAAO,OAAO,IAAI,sBAAsB;EACvC,SAAS,UAAU,OAAO;EAC1B,QAAQ,OAAO,SAAS,KAAK,aAAa;GACzC,MAAM,CAAC;GACP,SAAS,GAAG,QAAQ,QAAQ,IAAI,QAAQ,KAAK;GAC7C,UAAU,GAAG,QAAQ,KAAK,GAAG,QAAQ,KAAK,GAAG,QAAQ;EACtD,EAAE;CACH,CAAC;CAEF,OAAO,eAAe,OAAO,KAAK;AACnC,CAAC;;;;AAKD,MAAa,+BACZ,MACA,UAAuD,CAAC,MAExD,eAAe,SAAS,uBAAuB,MAAM,OAAO,GAAG,EAAE,WAAW,QAAQ,aAAa,KAAK,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { n as processEnv, r as readEnv } from "./env-R3_XPW_u.mjs";
|
|
2
|
+
import { r as validate } from "./schema-DcD4N9N-.mjs";
|
|
3
|
+
import { Effect } from "effect";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import process from "node:process";
|
|
6
|
+
// src/env.ts
|
|
7
|
+
const decodeEnv = Effect.fn("decodeEnv")(function* (schema, options) {
|
|
8
|
+
const env = options.env ?? processEnv();
|
|
9
|
+
const environment = options.environment ?? (env.NODE_ENV || void 0);
|
|
10
|
+
const result = yield* readEnv(join(options.cwd ?? process.cwd(), ".env"), {
|
|
11
|
+
environment,
|
|
12
|
+
env
|
|
13
|
+
});
|
|
14
|
+
return yield* validate(schema, result.variables, {
|
|
15
|
+
subject: "environment variables",
|
|
16
|
+
async: options.async,
|
|
17
|
+
native: options.native,
|
|
18
|
+
issues: result.problems.map((problem) => ({
|
|
19
|
+
path: [],
|
|
20
|
+
message: problem.message,
|
|
21
|
+
location: `${problem.file}:${problem.line}:${problem.column}`
|
|
22
|
+
}))
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
export { decodeEnv as t };
|
|
26
|
+
|
|
27
|
+
//# sourceMappingURL=env-Bd-1Atoz.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-Bd-1Atoz.mjs","names":[],"sources":["../src/env.ts"],"sourcesContent":["import { join } from 'node:path';\nimport process from 'node:process';\n\nimport { Effect } from 'effect';\n\nimport { processEnv, readEnv } from './core/env.ts';\nimport { validate } from './schema.ts';\n\nimport type { ConfigSchema, LoadEnvOptions } from './schema.ts';\n\nexport const decodeEnv = Effect.fn('decodeEnv')(function* <S extends ConfigSchema>(\n\tschema: S,\n\toptions: LoadEnvOptions & { readonly async: boolean; readonly native?: boolean | undefined },\n) {\n\tconst env = options.env ?? processEnv();\n\tconst environment = options.environment ?? (env.NODE_ENV || undefined);\n\tconst result = yield* readEnv(join(options.cwd ?? process.cwd(), '.env'), { environment, env });\n\treturn yield* validate(schema, result.variables, {\n\t\tsubject: 'environment variables',\n\t\tasync: options.async,\n\t\tnative: options.native,\n\t\tissues: result.problems.map((problem) => ({\n\t\t\tpath: [],\n\t\t\tmessage: problem.message,\n\t\t\tlocation: `${problem.file}:${problem.line}:${problem.column}`,\n\t\t})),\n\t});\n});\n"],"mappings":";;;;;;AAUA,MAAa,YAAY,OAAO,GAAG,WAAW,CAAC,CAAC,WAC/C,QACA,SACC;CACD,MAAM,MAAM,QAAQ,OAAO,WAAW;CACtC,MAAM,cAAc,QAAQ,gBAAgB,IAAI,YAAY,KAAA;CAC5D,MAAM,SAAS,OAAO,QAAQ,KAAK,QAAQ,OAAO,QAAQ,IAAI,GAAG,MAAM,GAAG;EAAE;EAAa;CAAI,CAAC;CAC9F,OAAO,OAAO,SAAS,QAAQ,OAAO,WAAW;EAChD,SAAS;EACT,OAAO,QAAQ;EACf,QAAQ,QAAQ;EAChB,QAAQ,OAAO,SAAS,KAAK,aAAa;GACzC,MAAM,CAAC;GACP,SAAS,QAAQ;GACjB,UAAU,GAAG,QAAQ,KAAK,GAAG,QAAQ,KAAK,GAAG,QAAQ;EACtD,EAAE;CACH,CAAC;AACF,CAAC"}
|