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.
Files changed (96) hide show
  1. package/README.md +184 -3
  2. package/dist/bun.d.mts +7 -0
  3. package/dist/bun.mjs +36 -0
  4. package/dist/bun.mjs.map +1 -0
  5. package/dist/effect.browser-Bhr-Yr7t.d.mts +61 -0
  6. package/dist/effect.browser-CO87TEE_.mjs +50 -0
  7. package/dist/effect.browser-CO87TEE_.mjs.map +1 -0
  8. package/dist/effect.browser.d.mts +5 -0
  9. package/dist/effect.browser.mjs +3 -0
  10. package/dist/effect.d.mts +6 -60
  11. package/dist/effect.mjs +7 -48
  12. package/dist/effect.mjs.map +1 -1
  13. package/dist/env-Bd-1Atoz.mjs +27 -0
  14. package/dist/env-Bd-1Atoz.mjs.map +1 -0
  15. package/dist/env-R3_XPW_u.mjs +78 -0
  16. package/dist/env-R3_XPW_u.mjs.map +1 -0
  17. package/dist/formats-D_dKXoPf.mjs +900 -0
  18. package/dist/formats-D_dKXoPf.mjs.map +1 -0
  19. package/dist/{resolve-BeFHJeZb.d.mts → index-jUM0zKD5.d.mts} +10 -52
  20. package/dist/index.browser-BUi5xv0g.d.mts +15 -0
  21. package/dist/index.browser-C5z_VT_3.mjs +17 -0
  22. package/dist/index.browser-C5z_VT_3.mjs.map +1 -0
  23. package/dist/index.browser.d.mts +5 -0
  24. package/dist/index.browser.mjs +3 -0
  25. package/dist/index.d.mts +5 -14
  26. package/dist/index.mjs +3 -12
  27. package/dist/index.mjs.map +1 -1
  28. package/dist/load-C-kweoyY.d.mts +53 -0
  29. package/dist/load-DxIjRpX6.mjs +127 -0
  30. package/dist/load-DxIjRpX6.mjs.map +1 -0
  31. package/dist/mapper.mjs +5 -2
  32. package/dist/mapper.mjs.map +1 -1
  33. package/dist/plugins/esbuild.d.mts +5 -0
  34. package/dist/plugins/esbuild.mjs +6 -0
  35. package/dist/plugins/esbuild.mjs.map +1 -0
  36. package/dist/plugins/jest.d.mts +10 -0
  37. package/dist/plugins/jest.mjs +29 -0
  38. package/dist/plugins/jest.mjs.map +1 -0
  39. package/dist/plugins/loader.d.mts +16 -0
  40. package/dist/plugins/loader.mjs +27 -0
  41. package/dist/plugins/loader.mjs.map +1 -0
  42. package/dist/plugins/next.d.mts +21 -0
  43. package/dist/plugins/next.mjs +126 -0
  44. package/dist/plugins/next.mjs.map +1 -0
  45. package/dist/plugins/rolldown.d.mts +5 -0
  46. package/dist/plugins/rolldown.mjs +6 -0
  47. package/dist/plugins/rolldown.mjs.map +1 -0
  48. package/dist/plugins/rollup.d.mts +5 -0
  49. package/dist/plugins/rollup.mjs +6 -0
  50. package/dist/plugins/rollup.mjs.map +1 -0
  51. package/dist/plugins/rsbuild.d.mts +5 -0
  52. package/dist/plugins/rsbuild.mjs +6 -0
  53. package/dist/plugins/rsbuild.mjs.map +1 -0
  54. package/dist/plugins/rspack.d.mts +5 -0
  55. package/dist/plugins/rspack.mjs +6 -0
  56. package/dist/plugins/rspack.mjs.map +1 -0
  57. package/dist/plugins/vite.d.mts +5 -0
  58. package/dist/plugins/vite.mjs +6 -0
  59. package/dist/plugins/vite.mjs.map +1 -0
  60. package/dist/plugins/webpack.d.mts +5 -0
  61. package/dist/plugins/webpack.mjs +6 -0
  62. package/dist/plugins/webpack.mjs.map +1 -0
  63. package/dist/positions-BCTEFZTX.mjs +89 -0
  64. package/dist/positions-BCTEFZTX.mjs.map +1 -0
  65. package/dist/problems-BNAqmOzL.mjs +23 -0
  66. package/dist/problems-BNAqmOzL.mjs.map +1 -0
  67. package/dist/register.d.mts +1 -0
  68. package/dist/register.mjs +26 -0
  69. package/dist/register.mjs.map +1 -0
  70. package/dist/resolve-D3geHCGs.mjs +242 -0
  71. package/dist/resolve-D3geHCGs.mjs.map +1 -0
  72. package/dist/runtime/index.d.mts +29 -35
  73. package/dist/runtime/index.mjs +4 -99
  74. package/dist/runtime-DeRUtPZ9.mjs +69 -0
  75. package/dist/runtime-DeRUtPZ9.mjs.map +1 -0
  76. package/dist/{schema-CNquZBC8.d.mts → schema-BsuU_Hup.d.mts} +2 -2
  77. package/dist/{schema-C31aywfc.mjs → schema-DcD4N9N-.mjs} +4 -24
  78. package/dist/schema-DcD4N9N-.mjs.map +1 -0
  79. package/dist/shared-BnK77Zcp.mjs +55 -0
  80. package/dist/shared-BnK77Zcp.mjs.map +1 -0
  81. package/dist/shared-CZ44uwQq.d.mts +23 -0
  82. package/dist/source-BWf_Y0YZ.d.mts +32 -0
  83. package/dist/source-CcI86kKk.mjs +9 -0
  84. package/dist/source-CcI86kKk.mjs.map +1 -0
  85. package/dist/sync-B-HAK-_E.mjs +14 -0
  86. package/dist/sync-B-HAK-_E.mjs.map +1 -0
  87. package/dist/unplugin-AC_HM_T2.d.mts +2 -0
  88. package/dist/unplugin-BQGm_bhA.mjs +83 -0
  89. package/dist/unplugin-BQGm_bhA.mjs.map +1 -0
  90. package/package.json +133 -12
  91. package/dist/placeholders-MqW2KO9Z.mjs +0 -511
  92. package/dist/placeholders-MqW2KO9Z.mjs.map +0 -1
  93. package/dist/resolve-CDWA58rz.mjs +0 -326
  94. package/dist/resolve-CDWA58rz.mjs.map +0 -1
  95. package/dist/runtime/index.mjs.map +0 -1
  96. package/dist/schema-C31aywfc.mjs.map +0 -1
package/README.md CHANGED
@@ -2,22 +2,203 @@
2
2
 
3
3
  # tyfigo
4
4
 
5
- **Import YAML, TOML, JSON5, INI and .env files into TypeScript with full type inference.**
5
+ **Typed, validated application config for TypeScript.**
6
6
 
7
7
  [![GitHub](https://img.shields.io/github/license/imranbarbhuiya/tyfigo)](https://github.com/imranbarbhuiya/tyfigo/blob/main/LICENSE)
8
8
  [![npm](https://img.shields.io/npm/v/tyfigo?color=crimson&logo=npm&style=flat-square)](https://www.npmjs.com/package/tyfigo)
9
9
 
10
10
  </div>
11
11
 
12
- > [!WARNING]
13
- > tyfigo is in early development and not usable yet.
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
@@ -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 { d as ResolvedLayer, f as LocatedSpan, g as Span, h as Path, l as ResolveOptions, m as ProblemCode, p as Problem, r as ConfigSource, u as ResolvedConfig } from "./resolve-BeFHJeZb.mjs";
2
- import { a as LooseInput, i as LoadEnvOptions, n as ConfigSchema, o as SchemaInput, r as ConfigValidationError, s as SchemaOutput, t as ConfigIssue } from "./schema-CNquZBC8.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
- 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 { o as resolveConfig } from "./resolve-CDWA58rz.mjs";
2
- import { n as decodeConfig, r as decodeEnv, t as ConfigValidationError } from "./schema-C31aywfc.mjs";
3
- import { ConfigProvider, Context, Effect, Layer } from "effect";
4
- // src/effect/load.ts
5
- /**
6
- * The error of every tyfigo Effect: a config or the environment does not match its schema, or a config file could not
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 });
@@ -1 +1 @@
1
- {"version":3,"file":"effect.mjs","names":[],"sources":["../src/effect/load.ts","../src/effect/provider.ts"],"sourcesContent":["import { Context, Layer } from 'effect';\n\nimport { ConfigValidationError, decodeConfig, decodeEnv } from '../schema.ts';\n\nimport type { ConfigSchema, LoadEnvOptions, 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 * 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 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 * 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\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, Effect } from 'effect';\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 { 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 * 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 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\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":";;;;;;;;AA0BA,MAAa,cAAc;;;;;AAO3B,MAAa,cACZ,QACA,WAEA,aAAa,QAAQ,QAAwB;CAAE,OAAO;CAAM,QAAQ;AAAK,CAAC;;;;;AAU3E,MAAa,WACZ,QACA,UAA0B,CAAC,MAE3B,UAAU,QAAwB;CAAE,GAAG;CAAS,OAAO;CAAM,QAAQ;AAAK,CAAC;;;;AAS5E,MAAa,eACZ,SACA,QACA,WACoD,MAAM,OAAO,SAAS,WAAW,QAAQ,MAAM,CAAC;;;;AAKrG,MAAa,YACZ,SACA,QACA,UAA0B,CAAC,MACyB,MAAM,OAAO,SAAS,QAAQ,QAAQ,OAAO,CAAC;;;;;;;;;;;;;AAcnG,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;;;;;;AC7ED,MAAa,kBAAkB,WAC9B,eAAe,YAAY,QAAQ,EAAE,sBAAsB,KAAK,CAAC;;;;;AAMlE,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;;;;;AAMD,MAAa,uBAAuB,QAAiB,UAAsC,CAAC,MAC3F,eAAe,SAAS,eAAe,MAAM,GAAG,EAAE,WAAW,QAAQ,aAAa,KAAK,CAAC;;;;AAKzF,MAAa,+BACZ,MACA,UAAuD,CAAC,MAExD,eAAe,SAAS,uBAAuB,MAAM,OAAO,GAAG,EAAE,WAAW,QAAQ,aAAa,KAAK,CAAC"}
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"}