@konfig.ts/env 0.0.2 → 0.0.4
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 +60 -72
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,100 +1,88 @@
|
|
|
1
1
|
# @konfig.ts/env
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
One declaration for a pod's environment, shared by both sides of the wire: the
|
|
4
|
+
Kubernetes manifest that injects the env vars, and the app code that decodes
|
|
5
|
+
them at startup. Name `DATABASE_URL` once — rename it in one place and the
|
|
6
|
+
typechecker flags every consumer.
|
|
5
7
|
|
|
6
|
-
##
|
|
8
|
+
## Install
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
```bash
|
|
11
|
+
bun add @konfig.ts/env
|
|
12
|
+
```
|
|
9
13
|
|
|
10
|
-
|
|
11
|
-
`valueEnv`, `configMapEnv`).
|
|
12
|
-
- The pod's runtime code (`Config.redacted("DATABASE_URL")`).
|
|
14
|
+
## Usage
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
both ends into one declaration:
|
|
16
|
+
Declare the contract once, in a module both sides import:
|
|
16
17
|
|
|
17
18
|
```ts
|
|
18
|
-
|
|
19
|
-
import { defineEnvironment, defineLiteral, defineSecret } from "@konfig.ts/env"
|
|
19
|
+
import { Downward, Environment, Literal, Secret } from "@konfig.ts/env"
|
|
20
20
|
|
|
21
|
-
export const dbCreds =
|
|
21
|
+
export const dbCreds = Secret.define({
|
|
22
22
|
name: "db-creds",
|
|
23
23
|
namespace: "prod",
|
|
24
|
-
env: {
|
|
25
|
-
url: "DATABASE_URL",
|
|
26
|
-
password: "DATABASE_PASSWORD"
|
|
27
|
-
}
|
|
24
|
+
env: { url: "DATABASE_URL", password: "DATABASE_PASSWORD" }
|
|
28
25
|
})
|
|
29
26
|
|
|
30
|
-
export const
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
export const apiEnv = Environment.define({
|
|
28
|
+
db: dbCreds,
|
|
29
|
+
http: Environment.define({
|
|
30
|
+
port: Literal.define({ envName: "HTTP_PORT", value: 8080 }),
|
|
31
|
+
logLevel: Literal.define({ envName: "LOG_LEVEL", value: "info" })
|
|
32
|
+
}),
|
|
33
|
+
runtime: Environment.define({
|
|
34
|
+
nodeEnv: Literal.define({ envName: "NODE_ENV", value: "production" }),
|
|
35
|
+
podName: Downward.define({ envName: "POD_NAME", fieldPath: "metadata.name" })
|
|
36
|
+
})
|
|
37
|
+
})
|
|
33
38
|
```
|
|
34
39
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
Every `defineSecret` / `defineLiteral` / `defineDownward` call returns
|
|
38
|
-
an **entry** — a yieldable Effect `Config<...>` intersected with the
|
|
39
|
-
pure binding metadata (`name`, `namespace`, `env`/`envName`). Each
|
|
40
|
-
entry is its own state-management-style atom: pull it standalone, or
|
|
41
|
-
group it.
|
|
42
|
-
|
|
43
|
-
`defineEnvironment({...})` is a **bundle** — also a yieldable Config,
|
|
44
|
-
with `.members` re-exposing each named entry.
|
|
40
|
+
Then use the same `apiEnv` from both sides. `bind` (manifest) and `runtime`
|
|
41
|
+
(process) both live in `@konfig.ts/k8s`:
|
|
45
42
|
|
|
46
43
|
```ts
|
|
47
|
-
//
|
|
48
|
-
import {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
44
|
+
// infra module — emit the Deployment env block + the secret backend's CRs
|
|
45
|
+
import { Environment } from "@konfig.ts/k8s"
|
|
46
|
+
const bound = Environment.bind({ env: apiEnv, namespace: "prod", secrets: { db: { backend } } })
|
|
47
|
+
// bound.envVars → container env; bound.manifests → the CRs
|
|
48
|
+
|
|
49
|
+
// app process — decode the same vars at startup, into a typed record
|
|
50
|
+
import { Environment } from "@konfig.ts/k8s"
|
|
51
|
+
import { Effect } from "effect"
|
|
52
|
+
const config = await Effect.runPromise(Environment.runtime(apiEnv))
|
|
53
|
+
console.log(`listening on :${config.http.port}`)
|
|
56
54
|
```
|
|
57
55
|
|
|
58
|
-
##
|
|
56
|
+
## Atoms
|
|
59
57
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
| Constructor | Produces |
|
|
59
|
+
| -------------------- | ---------------------------------------------------------------------------------- |
|
|
60
|
+
| `Secret.define` | a secret contract (`{ name, namespace, env }`), bound to a backend at compose time |
|
|
61
|
+
| `Literal.define` | a constant (`{ envName, value, schema? }`) baked into the manifest |
|
|
62
|
+
| `Downward.define` | a Kubernetes downward-API field (`{ envName, fieldPath }`) |
|
|
63
|
+
| `Environment.define` | a bundle of the above — nestable; the single source of truth for both sides |
|
|
64
|
+
| `SecretSource` | plaintext sources for backends: `.fromConfig`, `.literal`, `.fromCommand` |
|
|
64
65
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
import { Environment, Secret, Workload } from "@konfig.ts/k8s"
|
|
68
|
-
|
|
69
|
-
const apiEnvK8s = Environment.bind({ env: apiEnv })
|
|
70
|
-
|
|
71
|
-
Workload.web({
|
|
72
|
-
/* ... */
|
|
73
|
-
deployment: {
|
|
74
|
-
containers: [{ name: "api", image: "...", env: apiEnvK8s.envVars }]
|
|
75
|
-
}
|
|
76
|
-
})
|
|
77
|
-
```
|
|
66
|
+
Two members claiming the same `envName` is a compile-time error
|
|
67
|
+
(`EnvNameCollision`) — caught before it can silently shadow another.
|
|
78
68
|
|
|
79
|
-
##
|
|
69
|
+
## Internals
|
|
80
70
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
later phases.
|
|
71
|
+
An atom is a yieldable Effect `Config` intersected with its binding metadata; a
|
|
72
|
+
bundle is a `Config` over the whole tree. See the `Environment` section of
|
|
73
|
+
[`.docs/architecture.md`](../../.docs/architecture.md).
|
|
85
74
|
|
|
86
75
|
## Requirements
|
|
87
76
|
|
|
88
|
-
konfig.ts
|
|
89
|
-
|
|
90
|
-
|
|
77
|
+
konfig.ts is built on [Effect](https://effect.website/), currently in beta.
|
|
78
|
+
Until Effect ships a stable 4.x, install the exact beta konfig.ts is built
|
|
79
|
+
against:
|
|
91
80
|
|
|
92
|
-
- **`effect@4.0.0-beta.70`** — required.
|
|
93
|
-
- **`@effect/platform-node@4.0.0-beta.70`** — required only
|
|
94
|
-
(the Node filesystem/subprocess entrypoint); manifest-only
|
|
95
|
-
omit it.
|
|
81
|
+
- **`effect@4.0.0-beta.70`** — required by every package.
|
|
82
|
+
- **`@effect/platform-node@4.0.0-beta.70`** — required only when you call
|
|
83
|
+
`render()` (the Node filesystem/subprocess entrypoint); manifest-only
|
|
84
|
+
consumers can omit it (it is declared as an optional peer).
|
|
96
85
|
|
|
97
|
-
The
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
relax to a caret range once Effect reaches a stable 4.x.
|
|
86
|
+
The pin is exact on purpose: Effect's beta line makes breaking changes between
|
|
87
|
+
builds, so a looser range surfaces as `ERESOLVE` install conflicts. It relaxes
|
|
88
|
+
to a caret range once Effect reaches a stable 4.x.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@konfig.ts/env",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Yieldable environment atoms (defineSecret, defineLiteral, defineDownward, defineEnvironment) shared between konfig manifest emission and the runtime pod application.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "David Stahl-Gruber",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"postpack": "node ../../scripts/prepack-exports.mjs restore"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@konfig.ts/core": "0.0.
|
|
54
|
+
"@konfig.ts/core": "0.0.4"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"effect": "4.0.0-beta.70"
|