tyfigo 0.1.0-next.ffecb89.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 +182 -3
- package/package.json +14 -2
package/README.md
CHANGED
|
@@ -2,24 +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.
|
|
14
17
|
|
|
15
18
|
Read the documentation at [tyfigo.vercel.app](https://tyfigo.vercel.app).
|
|
16
19
|
|
|
17
20
|
## Install
|
|
18
21
|
|
|
22
|
+
tyfigo needs TypeScript 7.1, the first release with content mappers, and Node 24 or later (or Bun).
|
|
23
|
+
|
|
19
24
|
```bash
|
|
20
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;
|
|
103
|
+
```
|
|
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'.
|
|
21
122
|
```
|
|
22
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
|
+
|
|
23
202
|
## Buy me some doughnuts
|
|
24
203
|
|
|
25
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/package.json
CHANGED
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tyfigo",
|
|
3
|
-
"version": "0.1.0
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Typed, validated application config for TypeScript: import layered YAML, TOML, JSONC, JSON5, INI, .properties and XML config files, typed by a TypeScript 7.1 content mapper",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"config",
|
|
7
|
+
"configuration",
|
|
7
8
|
"content-mapper",
|
|
8
9
|
"dotenv",
|
|
9
10
|
"effect",
|
|
11
|
+
"env",
|
|
12
|
+
"esbuild",
|
|
10
13
|
"ini",
|
|
11
14
|
"json5",
|
|
15
|
+
"jsonc",
|
|
16
|
+
"nextjs",
|
|
17
|
+
"properties",
|
|
18
|
+
"rollup",
|
|
19
|
+
"rspack",
|
|
20
|
+
"standard-schema",
|
|
12
21
|
"toml",
|
|
13
22
|
"typescript",
|
|
23
|
+
"vite",
|
|
24
|
+
"webpack",
|
|
25
|
+
"xml",
|
|
14
26
|
"yaml"
|
|
15
27
|
],
|
|
16
28
|
"homepage": "https://tyfigo.vercel.app",
|