konfeeg 0.0.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 +155 -0
- package/dist/index.d.mts +233 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +190 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# konfeeg
|
|
2
|
+
|
|
3
|
+
Validated, strongly-typed config for Node and the browser. Define a schema once; values are resolved, coerced, and validated at startup — missing or invalid values throw immediately.
|
|
4
|
+
|
|
5
|
+
> **Note:** Throws at startup if any required value is missing or fails format validation — broken `.env` files surface immediately, not at runtime.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Quick start
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { createEnvironmentConfig } from "konfeeg"
|
|
13
|
+
|
|
14
|
+
// 1. Declare your environments (required vs optional)
|
|
15
|
+
type MyEnvs = {
|
|
16
|
+
dev?: unknown // optional (?) = per-env value may be omitted
|
|
17
|
+
staging: unknown // required = must supply a value
|
|
18
|
+
production: unknown
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// 2. Build the config — note the extra () 👇 (curried for TS inference)
|
|
22
|
+
const config = createEnvironmentConfig<MyEnvs>()("staging", {
|
|
23
|
+
apiUrl: {
|
|
24
|
+
doc: "Base URL for the API",
|
|
25
|
+
format: "url", // Will error if the value isn't a valid URL
|
|
26
|
+
processEnv: "API_URL", // runtime override (highest priority)
|
|
27
|
+
dev: "http://localhost:3000",
|
|
28
|
+
staging: "https://staging-api.example.com",
|
|
29
|
+
production: "https://api.example.com",
|
|
30
|
+
},
|
|
31
|
+
mongo: {
|
|
32
|
+
dbName: {
|
|
33
|
+
doc: "Mongo database name",
|
|
34
|
+
format: String, // Will error if the value isn't a string
|
|
35
|
+
processEnv: "MONGO_DB_NAME",
|
|
36
|
+
value: "my-app-db", // static fallback (lowest priority)
|
|
37
|
+
},
|
|
38
|
+
password: {
|
|
39
|
+
doc: "Mongo database password",
|
|
40
|
+
format: String,
|
|
41
|
+
// runtime-only, no static fallback
|
|
42
|
+
importMetaEnv: "MONGO_PASSWORD", // uses import.meta.env instead of process.env (e.g. for Vite)
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
config.env // "staging"
|
|
48
|
+
config.apiUrl // string (validated as URL)
|
|
49
|
+
config.mongo.dbName // string
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Schema fields
|
|
55
|
+
|
|
56
|
+
| Field | Required | Description |
|
|
57
|
+
| ---------------------------- | -------- | ---------------------------------------------------------------------- |
|
|
58
|
+
| `doc` | required | Human-readable description |
|
|
59
|
+
| `format` | required | Validation format — see below |
|
|
60
|
+
| `value` | optional | Constant shared across all environments (lowest priority) |
|
|
61
|
+
| `processEnv` | optional | `process.env` key — runtime override (highest priority) |
|
|
62
|
+
| `importMetaEnv` | optional | `import.meta.env` key — runtime override (highest priority) |
|
|
63
|
+
| `optional` | optional | When `true`, missing value resolves to `undefined` instead of throwing |
|
|
64
|
+
| `default` | optional | Fallback when `optional: true` and no value is found |
|
|
65
|
+
| env keys (`dev`, `staging`…) | optional | Per-environment value overrides |
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Formats
|
|
70
|
+
|
|
71
|
+
Validate that the resolved value matches the declared format. Coercion is applied where reasonable (e.g. numeric strings → numbers, `'true'`/`'false'` → booleans).
|
|
72
|
+
|
|
73
|
+
| Format | Resolved type | Notes |
|
|
74
|
+
| ------------ | ------------- | ------------------------------------------ |
|
|
75
|
+
| `String` | `string` | Value must be a string |
|
|
76
|
+
| `Number` | `number` | Numeric strings are coerced |
|
|
77
|
+
| `Boolean` | `boolean` | `'true'`/`'false'` and `0`/`1` are coerced |
|
|
78
|
+
| `Array` | `any[]` | Value must be an array |
|
|
79
|
+
| `'url'` | `string` | Must parse as a valid URL |
|
|
80
|
+
| `['a', 'b']` | `'a' \| 'b'` | Value must be one of the listed literals |
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Value resolution order
|
|
85
|
+
|
|
86
|
+
When multiple sources are declared on the same entry, the highest-priority source wins.
|
|
87
|
+
|
|
88
|
+
| Priority | Source | Use for |
|
|
89
|
+
| ----------- | ---------------------------------- | -------------------------------- |
|
|
90
|
+
| 3 — highest | `processEnv` / `importMetaEnv` | Secrets, local overrides |
|
|
91
|
+
| 2 | Per-env fields (`dev`, `staging`…) | Environment-specific values |
|
|
92
|
+
| 1 — lowest | `value` | Constants shared across all envs |
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Defining environments
|
|
97
|
+
|
|
98
|
+
Declare a plain object type. Required properties mean every entry that supplies per-env values must include that env. Optional properties (`?`) may be omitted.
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
type MyEnvs = {
|
|
102
|
+
dev?: unknown // optional — per-env value may be omitted
|
|
103
|
+
integ?: unknown
|
|
104
|
+
staging: unknown // required — every entry must supply a value
|
|
105
|
+
production: unknown
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Fallbacks
|
|
112
|
+
|
|
113
|
+
When an entry has no value for the active environment, resolution can fall back to another env's value. Chains are transitive. Only affects per-env resolution (priority 2) — runtime env vars still win.
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
const config = createEnvironmentConfig<MyEnvs>()(
|
|
117
|
+
"dev",
|
|
118
|
+
{
|
|
119
|
+
apiUrl: {
|
|
120
|
+
doc: "API URL",
|
|
121
|
+
format: "url",
|
|
122
|
+
// no `dev` field — falls back to `integ`
|
|
123
|
+
integ: "https://integ.example.com",
|
|
124
|
+
staging: "https://staging.example.com",
|
|
125
|
+
production: "https://api.example.com",
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
fallbacks: {
|
|
130
|
+
dev: "integ", // dev → integ
|
|
131
|
+
integ: "staging", // integ → staging (chained)
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
config.apiUrl // "https://integ.example.com"
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
A circular fallback chain (e.g. `{ dev: 'integ', integ: 'dev' }`) throws synchronously with the cycle path in the error message.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## `defineEnvironmentConfig`
|
|
144
|
+
|
|
145
|
+
Same as `createEnvironmentConfig`, but binds the schema first and the environment later — useful when the environment isn't known at schema-definition time.
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
import { defineEnvironmentConfig } from "konfeeg"
|
|
149
|
+
|
|
150
|
+
const buildConfig = defineEnvironmentConfig<MyEnvs>()({
|
|
151
|
+
/* schema */
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
const config = buildConfig(process.env.APP_ENV as any)
|
|
155
|
+
```
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
//#region lib/util-types.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Declaration of the environments a config supports: an object whose keys
|
|
4
|
+
* are environment names, with required envs as required properties and
|
|
5
|
+
* optional envs as optional properties.
|
|
6
|
+
*
|
|
7
|
+
* The property value type is not used by the library — only the keys and
|
|
8
|
+
* their required/optional status matter — so `unknown` (or any other type)
|
|
9
|
+
* is fine.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* type MyEnvs = {
|
|
14
|
+
* dev?: unknown
|
|
15
|
+
* integ?: unknown
|
|
16
|
+
* staging: unknown
|
|
17
|
+
* production: unknown
|
|
18
|
+
* }
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
type EnvsShape = Record<string, any>;
|
|
22
|
+
type RequiredKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? never : K }[keyof T] & string;
|
|
23
|
+
type OptionalKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? K : never }[keyof T] & string;
|
|
24
|
+
/** Union of all environment names declared on `E` (required ∪ optional). */
|
|
25
|
+
type EnvName<E extends EnvsShape> = keyof E & string;
|
|
26
|
+
/**
|
|
27
|
+
* Per-environment value shape for a value of type `T`:
|
|
28
|
+
* required envs are required, optional envs are optional.
|
|
29
|
+
*/
|
|
30
|
+
type PerEnv<E extends EnvsShape, T> = { [K in RequiredKeys<E>]: T } & { [K in OptionalKeys<E>]?: T };
|
|
31
|
+
/**
|
|
32
|
+
* Map of environment names to a fallback environment name.
|
|
33
|
+
*
|
|
34
|
+
* If a per-environment value is not declared for the active environment on
|
|
35
|
+
* a given config entry, resolution falls back to the value declared for the
|
|
36
|
+
* environment named here. Fallbacks chain transitively until a value is
|
|
37
|
+
* found or the chain ends.
|
|
38
|
+
*
|
|
39
|
+
* Both keys and values must be names declared on the envs shape `E`.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* type MyEnvs = {
|
|
44
|
+
* dev?: unknown
|
|
45
|
+
* integ?: unknown
|
|
46
|
+
* staging: unknown
|
|
47
|
+
* production: unknown
|
|
48
|
+
* }
|
|
49
|
+
*
|
|
50
|
+
* // When running in `dev`, fall back to `integ`; if `integ` is also
|
|
51
|
+
* // missing a value, fall back to `staging`.
|
|
52
|
+
* const fallbacks: Fallbacks<MyEnvs> = {
|
|
53
|
+
* dev: 'integ',
|
|
54
|
+
* integ: 'staging',
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
type Fallbacks<E extends EnvsShape> = Partial<Record<EnvName<E>, EnvName<E>>>;
|
|
59
|
+
/**
|
|
60
|
+
* Options accepted as the third argument to `createEnvironmentConfig` (and
|
|
61
|
+
* the second argument to `defineEnvironmentConfig`).
|
|
62
|
+
*/
|
|
63
|
+
type CreateConfigOptions<E extends EnvsShape> = {
|
|
64
|
+
/**
|
|
65
|
+
* Map of environment names to a fallback environment name. See
|
|
66
|
+
* {@link Fallbacks} for details.
|
|
67
|
+
*/
|
|
68
|
+
fallbacks?: Fallbacks<E>;
|
|
69
|
+
};
|
|
70
|
+
//#endregion
|
|
71
|
+
//#region lib/types.d.ts
|
|
72
|
+
type NoEnvKeys<E extends EnvsShape> = { [K in EnvName<E>]?: never };
|
|
73
|
+
type RuntimeSourceOptional<T> = {
|
|
74
|
+
value?: T;
|
|
75
|
+
processEnv?: never;
|
|
76
|
+
importMetaEnv?: never;
|
|
77
|
+
} | {
|
|
78
|
+
value?: T;
|
|
79
|
+
processEnv: string;
|
|
80
|
+
importMetaEnv?: never;
|
|
81
|
+
} | {
|
|
82
|
+
value?: T;
|
|
83
|
+
processEnv?: never;
|
|
84
|
+
importMetaEnv: string;
|
|
85
|
+
};
|
|
86
|
+
type ValueSourceRequired<T> = {
|
|
87
|
+
value: T;
|
|
88
|
+
processEnv?: never;
|
|
89
|
+
importMetaEnv?: never;
|
|
90
|
+
} | {
|
|
91
|
+
value?: T;
|
|
92
|
+
processEnv: string;
|
|
93
|
+
importMetaEnv?: never;
|
|
94
|
+
} | {
|
|
95
|
+
value?: T;
|
|
96
|
+
processEnv?: never;
|
|
97
|
+
importMetaEnv: string;
|
|
98
|
+
};
|
|
99
|
+
type ValueSource<T, E extends EnvsShape> = (PerEnv<E, T> & RuntimeSourceOptional<T>) | (NoEnvKeys<E> & ValueSourceRequired<T>);
|
|
100
|
+
type ConfigEntryBase<T, E extends EnvsShape> = {
|
|
101
|
+
doc: string;
|
|
102
|
+
optional?: boolean;
|
|
103
|
+
} & ValueSource<T, E>;
|
|
104
|
+
type ConfigGroup<E extends EnvsShape> = {
|
|
105
|
+
[key: string]: ConfigEntry<any, E> | ConfigGroup<E>;
|
|
106
|
+
};
|
|
107
|
+
type ResolveEntryType<E> = E extends {
|
|
108
|
+
format: StringConstructor;
|
|
109
|
+
} ? string : E extends {
|
|
110
|
+
format: NumberConstructor;
|
|
111
|
+
} ? number : E extends {
|
|
112
|
+
format: BooleanConstructor;
|
|
113
|
+
} ? boolean : E extends {
|
|
114
|
+
format: 'url';
|
|
115
|
+
} ? string : E extends {
|
|
116
|
+
format: (infer F)[];
|
|
117
|
+
} ? F : E extends {
|
|
118
|
+
format: ArrayConstructor;
|
|
119
|
+
} ? any[] : any;
|
|
120
|
+
type ResolveConfigGroup<G> = { [K in keyof G]: G[K] extends {
|
|
121
|
+
doc: string;
|
|
122
|
+
} ? ResolveEntryType<G[K]> : ResolveConfigGroup<G[K]> };
|
|
123
|
+
type ConfigEntry<T, E extends EnvsShape> = UntypedEntry<E> | StringEntry<E> | NumberEntry<E> | BooleanEntry<E> | ArrayEntry<T, E> | EnumEntry<T, E> | UrlEntry<E>;
|
|
124
|
+
type StringEntry<E extends EnvsShape> = ConfigEntryBase<string, E> & {
|
|
125
|
+
format: StringConstructor;
|
|
126
|
+
default?: string;
|
|
127
|
+
};
|
|
128
|
+
type NumberEntry<E extends EnvsShape> = ConfigEntryBase<number, E> & {
|
|
129
|
+
format: NumberConstructor;
|
|
130
|
+
default?: number;
|
|
131
|
+
};
|
|
132
|
+
type BooleanEntry<E extends EnvsShape> = ConfigEntryBase<boolean, E> & {
|
|
133
|
+
format: BooleanConstructor;
|
|
134
|
+
default?: boolean;
|
|
135
|
+
};
|
|
136
|
+
type ArrayEntry<T, E extends EnvsShape> = ConfigEntryBase<T[], E> & {
|
|
137
|
+
format: ArrayConstructor;
|
|
138
|
+
default?: T[];
|
|
139
|
+
};
|
|
140
|
+
type EnumEntry<T, E extends EnvsShape> = ConfigEntryBase<T, E> & {
|
|
141
|
+
format: T[];
|
|
142
|
+
default?: T;
|
|
143
|
+
};
|
|
144
|
+
type UrlEntry<E extends EnvsShape> = ConfigEntryBase<string, E> & {
|
|
145
|
+
format: "url";
|
|
146
|
+
default?: string;
|
|
147
|
+
};
|
|
148
|
+
type UntypedEntry<E extends EnvsShape> = ConfigEntryBase<any, E> & {
|
|
149
|
+
format?: never;
|
|
150
|
+
default?: any;
|
|
151
|
+
};
|
|
152
|
+
//#endregion
|
|
153
|
+
//#region lib/create-config.d.ts
|
|
154
|
+
/**
|
|
155
|
+
* Create a resolved, validated config for the given environment.
|
|
156
|
+
*
|
|
157
|
+
* Curried so the envs declaration is bound on the first call and the
|
|
158
|
+
* schema is inferred (giving you autocomplete) on the second call.
|
|
159
|
+
*
|
|
160
|
+
* @typeParam E - The envs shape describing required/optional environments.
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* ```ts
|
|
164
|
+
* type MyEnvs = {
|
|
165
|
+
* dev?: unknown
|
|
166
|
+
* staging: unknown
|
|
167
|
+
* production: unknown
|
|
168
|
+
* }
|
|
169
|
+
* const config = createEnvironmentConfig<MyEnvs>()('dev', {
|
|
170
|
+
* port: { doc: 'Port', format: Number, value: 3000 },
|
|
171
|
+
* })
|
|
172
|
+
* config.port // number
|
|
173
|
+
* ```
|
|
174
|
+
*
|
|
175
|
+
* @example Fallback environments
|
|
176
|
+
* ```ts
|
|
177
|
+
* // When running in `dev`, any entry that does not declare a `dev` value
|
|
178
|
+
* // falls back to the entry's `integ` value.
|
|
179
|
+
* const config = createEnvironmentConfig<MyEnvs>()(
|
|
180
|
+
* 'dev',
|
|
181
|
+
* {
|
|
182
|
+
* apiUrl: {
|
|
183
|
+
* doc: 'API URL',
|
|
184
|
+
* format: 'url',
|
|
185
|
+
* integ: 'https://integ.example.com',
|
|
186
|
+
* staging: 'https://staging.example.com',
|
|
187
|
+
* production: 'https://api.example.com',
|
|
188
|
+
* },
|
|
189
|
+
* },
|
|
190
|
+
* { fallbacks: { dev: 'integ' } },
|
|
191
|
+
* )
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
194
|
+
declare function createEnvironmentConfig<E extends EnvsShape>(): <G extends ConfigGroup<E>>(env: EnvName<E>, inputConfig: G, options?: CreateConfigOptions<E>) => ResolveConfigGroup<G> & {
|
|
195
|
+
env: EnvName<E>;
|
|
196
|
+
};
|
|
197
|
+
//#endregion
|
|
198
|
+
//#region lib/define-config.d.ts
|
|
199
|
+
/**
|
|
200
|
+
* Like {@link createEnvironmentConfig}, but binds the schema first and the
|
|
201
|
+
* environment later. Useful when the active environment is not known at
|
|
202
|
+
* schema-definition time.
|
|
203
|
+
*
|
|
204
|
+
* @typeParam E - The envs shape describing required/optional environments.
|
|
205
|
+
*
|
|
206
|
+
* @example
|
|
207
|
+
* ```ts
|
|
208
|
+
* type MyEnvs = {
|
|
209
|
+
* dev?: unknown
|
|
210
|
+
* staging: unknown
|
|
211
|
+
* production: unknown
|
|
212
|
+
* }
|
|
213
|
+
* const buildConfig = defineEnvironmentConfig<MyEnvs>()({
|
|
214
|
+
* port: { doc: 'Port', format: Number, value: 3000 },
|
|
215
|
+
* })
|
|
216
|
+
* const config = buildConfig('dev')
|
|
217
|
+
* ```
|
|
218
|
+
*
|
|
219
|
+
* @example Fallback environments
|
|
220
|
+
* ```ts
|
|
221
|
+
* const buildConfig = defineEnvironmentConfig<MyEnvs>()(
|
|
222
|
+
* { apiUrl: { doc: 'API URL', format: 'url', staging: 'https://staging' } },
|
|
223
|
+
* { fallbacks: { dev: 'staging' } },
|
|
224
|
+
* )
|
|
225
|
+
* const config = buildConfig('dev') // apiUrl resolved from `staging`
|
|
226
|
+
* ```
|
|
227
|
+
*/
|
|
228
|
+
declare function defineEnvironmentConfig<E extends EnvsShape>(): <G extends ConfigGroup<E>>(inputConfig: G, options?: CreateConfigOptions<E>) => ((env: EnvName<E>) => ResolveConfigGroup<G> & {
|
|
229
|
+
env: EnvName<E>;
|
|
230
|
+
});
|
|
231
|
+
//#endregion
|
|
232
|
+
export { type CreateConfigOptions, type EnvName, type EnvsShape, type Fallbacks, type PerEnv, createEnvironmentConfig, defineEnvironmentConfig };
|
|
233
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../lib/util-types.ts","../lib/types.ts","../lib/create-config.ts","../lib/define-config.ts"],"mappings":"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"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
//#region lib/format.ts
|
|
2
|
+
function validateAndCoerce(value, format, fullKey, errors) {
|
|
3
|
+
switch (format) {
|
|
4
|
+
case String:
|
|
5
|
+
if (typeof value !== "string") errors.push(`Config value for ${fullKey} must be a string`);
|
|
6
|
+
break;
|
|
7
|
+
case Number:
|
|
8
|
+
value = Number(value);
|
|
9
|
+
if (isNaN(value)) errors.push(`Config value for ${fullKey} must be a number`);
|
|
10
|
+
break;
|
|
11
|
+
case Boolean:
|
|
12
|
+
if (typeof value !== "boolean" && value !== "true" && value !== "false" && value !== 1 && value !== 0) errors.push(`Config value for ${fullKey} must be a boolean`);
|
|
13
|
+
value = value === "true" ? true : value === "false" ? false : Boolean(value);
|
|
14
|
+
break;
|
|
15
|
+
case Array:
|
|
16
|
+
if (!Array.isArray(value)) errors.push(`Config value for ${fullKey} must be an array`);
|
|
17
|
+
break;
|
|
18
|
+
case "url":
|
|
19
|
+
try {
|
|
20
|
+
new URL(value);
|
|
21
|
+
} catch {
|
|
22
|
+
errors.push(`Config value for ${fullKey} must be a valid URL; found "${value}"`);
|
|
23
|
+
}
|
|
24
|
+
break;
|
|
25
|
+
default: if (format instanceof Array) {
|
|
26
|
+
if (!format.includes(value)) errors.push(`Config value for ${fullKey} must be one of: [${format.join(", ")}]`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region lib/create-config.ts
|
|
33
|
+
/**
|
|
34
|
+
* Create a resolved, validated config for the given environment.
|
|
35
|
+
*
|
|
36
|
+
* Curried so the envs declaration is bound on the first call and the
|
|
37
|
+
* schema is inferred (giving you autocomplete) on the second call.
|
|
38
|
+
*
|
|
39
|
+
* @typeParam E - The envs shape describing required/optional environments.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* type MyEnvs = {
|
|
44
|
+
* dev?: unknown
|
|
45
|
+
* staging: unknown
|
|
46
|
+
* production: unknown
|
|
47
|
+
* }
|
|
48
|
+
* const config = createEnvironmentConfig<MyEnvs>()('dev', {
|
|
49
|
+
* port: { doc: 'Port', format: Number, value: 3000 },
|
|
50
|
+
* })
|
|
51
|
+
* config.port // number
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* @example Fallback environments
|
|
55
|
+
* ```ts
|
|
56
|
+
* // When running in `dev`, any entry that does not declare a `dev` value
|
|
57
|
+
* // falls back to the entry's `integ` value.
|
|
58
|
+
* const config = createEnvironmentConfig<MyEnvs>()(
|
|
59
|
+
* 'dev',
|
|
60
|
+
* {
|
|
61
|
+
* apiUrl: {
|
|
62
|
+
* doc: 'API URL',
|
|
63
|
+
* format: 'url',
|
|
64
|
+
* integ: 'https://integ.example.com',
|
|
65
|
+
* staging: 'https://staging.example.com',
|
|
66
|
+
* production: 'https://api.example.com',
|
|
67
|
+
* },
|
|
68
|
+
* },
|
|
69
|
+
* { fallbacks: { dev: 'integ' } },
|
|
70
|
+
* )
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
function createEnvironmentConfig() {
|
|
74
|
+
return (env, inputConfig, options) => buildConfig(env, inputConfig, options);
|
|
75
|
+
}
|
|
76
|
+
function buildConfig(env, inputConfig, options) {
|
|
77
|
+
const errors = [];
|
|
78
|
+
const envChain = resolveFallbackChain(env, options?.fallbacks);
|
|
79
|
+
function processConfig(config, keyPrefix) {
|
|
80
|
+
const output = {};
|
|
81
|
+
for (const [key, entry] of Object.entries(config)) {
|
|
82
|
+
if (key === "env") throw new Error(`Config key "env" is reserved and cannot be used. It will already be present by default.`);
|
|
83
|
+
const fullKey = keyPrefix ? `${keyPrefix}.${key}` : key;
|
|
84
|
+
if (!("doc" in entry)) {
|
|
85
|
+
output[key] = processConfig(entry, fullKey);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const configEntry = entry;
|
|
89
|
+
let value = "value" in configEntry ? configEntry.value : void 0;
|
|
90
|
+
for (const candidateEnv of envChain) {
|
|
91
|
+
const envValue = configEntry[candidateEnv];
|
|
92
|
+
if (envValue !== void 0) {
|
|
93
|
+
value = envValue;
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if ("processEnv" in configEntry) {
|
|
98
|
+
const runtimeOverride = typeof process !== "undefined" && process.env ? process.env[configEntry.processEnv] : void 0;
|
|
99
|
+
if (runtimeOverride !== void 0) value = runtimeOverride;
|
|
100
|
+
} else if ("importMetaEnv" in configEntry) {
|
|
101
|
+
const runtimeOverride = typeof import.meta !== "undefined" && import.meta.env ? import.meta.env[configEntry.importMetaEnv] : void 0;
|
|
102
|
+
if (runtimeOverride !== void 0) value = runtimeOverride;
|
|
103
|
+
}
|
|
104
|
+
const hasValueSource = value !== void 0 || "processEnv" in configEntry || "importMetaEnv" in configEntry;
|
|
105
|
+
if (value === void 0 && !hasValueSource) {
|
|
106
|
+
errors.push(`No value source declared for ${fullKey}. Supply a value using environment names, "value", "processEnv", or "importMetaEnv".`);
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (value === void 0) if (configEntry.optional) {
|
|
110
|
+
value = configEntry.default;
|
|
111
|
+
if (value === void 0) {
|
|
112
|
+
output[key] = void 0;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
errors.push(`Missing required config value for ${fullKey} in environment ${env}`);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
value = validateAndCoerce(value, configEntry.format, fullKey, errors);
|
|
120
|
+
output[key] = value;
|
|
121
|
+
}
|
|
122
|
+
return output;
|
|
123
|
+
}
|
|
124
|
+
const outputConfig = processConfig(inputConfig, "");
|
|
125
|
+
if (errors.length > 0) {
|
|
126
|
+
console.error("Environment config validation failed", errors);
|
|
127
|
+
throw new Error(`Environment config validation failed:\n${errors.join("\n")}`);
|
|
128
|
+
}
|
|
129
|
+
outputConfig.env = env;
|
|
130
|
+
return outputConfig;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Build the ordered list of environments to consult for per-environment
|
|
134
|
+
* value resolution. The active env is always first; each subsequent entry
|
|
135
|
+
* is the fallback target declared for the previous env. Throws if the
|
|
136
|
+
* chain is cyclic.
|
|
137
|
+
*/
|
|
138
|
+
function resolveFallbackChain(env, fallbacks) {
|
|
139
|
+
const chain = [env];
|
|
140
|
+
if (!fallbacks) return chain;
|
|
141
|
+
const seen = new Set([env]);
|
|
142
|
+
let current = env;
|
|
143
|
+
while (fallbacks[current] !== void 0) {
|
|
144
|
+
const next = fallbacks[current];
|
|
145
|
+
if (seen.has(next)) throw new Error(`Circular fallback chain detected: ${[...chain, next].join(" -> ")}`);
|
|
146
|
+
seen.add(next);
|
|
147
|
+
chain.push(next);
|
|
148
|
+
current = next;
|
|
149
|
+
}
|
|
150
|
+
return chain;
|
|
151
|
+
}
|
|
152
|
+
//#endregion
|
|
153
|
+
//#region lib/define-config.ts
|
|
154
|
+
/**
|
|
155
|
+
* Like {@link createEnvironmentConfig}, but binds the schema first and the
|
|
156
|
+
* environment later. Useful when the active environment is not known at
|
|
157
|
+
* schema-definition time.
|
|
158
|
+
*
|
|
159
|
+
* @typeParam E - The envs shape describing required/optional environments.
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* ```ts
|
|
163
|
+
* type MyEnvs = {
|
|
164
|
+
* dev?: unknown
|
|
165
|
+
* staging: unknown
|
|
166
|
+
* production: unknown
|
|
167
|
+
* }
|
|
168
|
+
* const buildConfig = defineEnvironmentConfig<MyEnvs>()({
|
|
169
|
+
* port: { doc: 'Port', format: Number, value: 3000 },
|
|
170
|
+
* })
|
|
171
|
+
* const config = buildConfig('dev')
|
|
172
|
+
* ```
|
|
173
|
+
*
|
|
174
|
+
* @example Fallback environments
|
|
175
|
+
* ```ts
|
|
176
|
+
* const buildConfig = defineEnvironmentConfig<MyEnvs>()(
|
|
177
|
+
* { apiUrl: { doc: 'API URL', format: 'url', staging: 'https://staging' } },
|
|
178
|
+
* { fallbacks: { dev: 'staging' } },
|
|
179
|
+
* )
|
|
180
|
+
* const config = buildConfig('dev') // apiUrl resolved from `staging`
|
|
181
|
+
* ```
|
|
182
|
+
*/
|
|
183
|
+
function defineEnvironmentConfig() {
|
|
184
|
+
const create = createEnvironmentConfig();
|
|
185
|
+
return (inputConfig, options) => (env) => create(env, inputConfig, options);
|
|
186
|
+
}
|
|
187
|
+
//#endregion
|
|
188
|
+
export { createEnvironmentConfig, defineEnvironmentConfig };
|
|
189
|
+
|
|
190
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../lib/format.ts","../lib/create-config.ts","../lib/define-config.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\n\nexport function validateAndCoerce(\n value: any,\n format: any,\n fullKey: string,\n errors: string[],\n): any {\n switch (format) {\n case String:\n if (typeof value !== \"string\") {\n errors.push(`Config value for ${fullKey} must be a string`)\n }\n break\n case Number:\n value = Number(value)\n if (isNaN(value))\n errors.push(`Config value for ${fullKey} must be a number`)\n break\n case Boolean:\n if (\n typeof value !== \"boolean\" &&\n value !== \"true\" &&\n value !== \"false\" &&\n value !== 1 &&\n value !== 0\n ) {\n errors.push(`Config value for ${fullKey} must be a boolean`)\n }\n value =\n value === \"true\" ? true : value === \"false\" ? false : Boolean(value)\n break\n case Array:\n if (!Array.isArray(value)) {\n errors.push(`Config value for ${fullKey} must be an array`)\n }\n break\n case \"url\":\n try {\n new URL(value)\n } catch {\n errors.push(\n `Config value for ${fullKey} must be a valid URL; found \"${value}\"`,\n )\n }\n break\n default:\n if (format instanceof Array) {\n if (!format.includes(value)) {\n errors.push(\n `Config value for ${fullKey} must be one of: [${format.join(\", \")}]`,\n )\n }\n }\n }\n\n return value\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport type {\n EnvsShape,\n EnvName,\n CreateConfigOptions,\n Fallbacks,\n} from \"./util-types.js\"\nimport type { ConfigGroup, ResolveConfigGroup } from \"./types.js\"\nimport { validateAndCoerce } from \"./format.js\"\n\n/**\n * Create a resolved, validated config for the given environment.\n *\n * Curried so the envs declaration is bound on the first call and the\n * schema is inferred (giving you autocomplete) on the second call.\n *\n * @typeParam E - The envs shape describing required/optional environments.\n *\n * @example\n * ```ts\n * type MyEnvs = {\n * dev?: unknown\n * staging: unknown\n * production: unknown\n * }\n * const config = createEnvironmentConfig<MyEnvs>()('dev', {\n * port: { doc: 'Port', format: Number, value: 3000 },\n * })\n * config.port // number\n * ```\n *\n * @example Fallback environments\n * ```ts\n * // When running in `dev`, any entry that does not declare a `dev` value\n * // falls back to the entry's `integ` value.\n * const config = createEnvironmentConfig<MyEnvs>()(\n * 'dev',\n * {\n * apiUrl: {\n * doc: 'API URL',\n * format: 'url',\n * integ: 'https://integ.example.com',\n * staging: 'https://staging.example.com',\n * production: 'https://api.example.com',\n * },\n * },\n * { fallbacks: { dev: 'integ' } },\n * )\n * ```\n */\nexport function createEnvironmentConfig<E extends EnvsShape>() {\n return <G extends ConfigGroup<E>>(\n env: EnvName<E>,\n inputConfig: G,\n options?: CreateConfigOptions<E>,\n ): ResolveConfigGroup<G> & { env: EnvName<E> } =>\n buildConfig<E, G>(env, inputConfig, options)\n}\n\nfunction buildConfig<E extends EnvsShape, G extends ConfigGroup<E>>(\n env: EnvName<E>,\n inputConfig: G,\n options?: CreateConfigOptions<E>,\n): ResolveConfigGroup<G> & { env: EnvName<E> } {\n const errors: string[] = []\n\n // Resolve the per-environment lookup chain once for the active env.\n // Throws synchronously on a circular fallback chain.\n const envChain = resolveFallbackChain<E>(env, options?.fallbacks)\n\n function processConfig(\n config: ConfigGroup<E>,\n keyPrefix: string,\n ): Record<string, any> {\n const output: Record<string, any> = {}\n\n for (const [key, entry] of Object.entries(config)) {\n if (key === \"env\") {\n throw new Error(\n `Config key \"env\" is reserved and cannot be used. It will already be present by default.`,\n )\n }\n\n const fullKey = keyPrefix ? `${keyPrefix}.${key}` : key\n\n if (!(\"doc\" in entry)) {\n output[key] = processConfig(entry as ConfigGroup<E>, fullKey)\n continue\n }\n\n const configEntry = entry as any\n\n // Value resolution — sources are evaluated in ascending priority order.\n // The highest-priority source that resolves to a defined value wins.\n //\n // Priority │ Source\n // ─────────┼────────────────────────────────────────────────────────────────\n // 1 (low) │ Static `value` — same value across all environments\n // 2 │ Per-environment field, walking the fallback chain\n // │ — overrides the static value for that specific environment\n // 3 (high) │ Runtime env var via `processEnv` or `importMetaEnv`\n // │ — always wins; intended for secrets and local dev overrides\n\n // Priority 1: static value (lowest precedence)\n let value: any = \"value\" in configEntry ? configEntry.value : undefined\n\n // Priority 2: per-environment value, walking the fallback chain.\n // The first env in the chain with a defined value wins.\n for (const candidateEnv of envChain) {\n const envValue = configEntry[candidateEnv]\n if (envValue !== undefined) {\n value = envValue\n break\n }\n }\n\n // Priority 3: runtime env var (highest precedence — always wins when defined)\n if (\"processEnv\" in configEntry) {\n const runtimeOverride =\n // @ts-expect-error process may not be defined in browser builds\n typeof process !== \"undefined\" && process.env\n ? // @ts-expect-error process may not be defined in browser builds\n process.env[configEntry.processEnv as string]\n : undefined\n if (runtimeOverride !== undefined) value = runtimeOverride\n } else if (\"importMetaEnv\" in configEntry) {\n const runtimeOverride =\n // @ts-expect-error import.meta.env may not be defined in Node builds\n typeof import.meta !== \"undefined\" && import.meta.env\n ? // @ts-expect-error import.meta.env may not be defined in Node builds\n import.meta.env[configEntry.importMetaEnv as string]\n : undefined\n if (runtimeOverride !== undefined) value = runtimeOverride\n }\n\n const hasValueSource =\n value !== undefined ||\n \"processEnv\" in configEntry ||\n \"importMetaEnv\" in configEntry\n\n if (value === undefined && !hasValueSource) {\n errors.push(\n `No value source declared for ${fullKey}. Supply a value using environment names, \"value\", \"processEnv\", or \"importMetaEnv\".`,\n )\n continue\n }\n\n if (value === undefined) {\n if (configEntry.optional) {\n value = configEntry.default\n if (value === undefined) {\n output[key] = undefined\n continue\n }\n } else {\n errors.push(\n `Missing required config value for ${fullKey} in environment ${env}`,\n )\n continue\n }\n }\n\n //\n // Format validation and coercion\n //\n value = validateAndCoerce(value, configEntry.format, fullKey, errors)\n\n output[key] = value\n }\n\n return output\n }\n\n const outputConfig = processConfig(inputConfig, \"\")\n\n if (errors.length > 0) {\n console.error(\"Environment config validation failed\", errors)\n throw new Error(\n `Environment config validation failed:\\n${errors.join(\"\\n\")}`,\n )\n }\n\n outputConfig.env = env\n return outputConfig as ResolveConfigGroup<G> & { env: EnvName<E> }\n}\n\n/**\n * Build the ordered list of environments to consult for per-environment\n * value resolution. The active env is always first; each subsequent entry\n * is the fallback target declared for the previous env. Throws if the\n * chain is cyclic.\n */\nfunction resolveFallbackChain<E extends EnvsShape>(\n env: EnvName<E>,\n fallbacks: Fallbacks<E> | undefined,\n): EnvName<E>[] {\n const chain: EnvName<E>[] = [env]\n if (!fallbacks) return chain\n\n const seen = new Set<string>([env])\n let current: EnvName<E> = env\n while (fallbacks[current] !== undefined) {\n const next = fallbacks[current] as EnvName<E>\n if (seen.has(next)) {\n throw new Error(\n `Circular fallback chain detected: ${[...chain, next].join(\" -> \")}`,\n )\n }\n seen.add(next)\n chain.push(next)\n current = next\n }\n return chain\n}\n","import type { EnvsShape, EnvName, CreateConfigOptions } from \"./util-types.js\"\nimport type { ConfigGroup, ResolveConfigGroup } from \"./types.js\"\nimport { createEnvironmentConfig } from \"./create-config.js\"\n\n/**\n * Like {@link createEnvironmentConfig}, but binds the schema first and the\n * environment later. Useful when the active environment is not known at\n * schema-definition time.\n *\n * @typeParam E - The envs shape describing required/optional environments.\n *\n * @example\n * ```ts\n * type MyEnvs = {\n * dev?: unknown\n * staging: unknown\n * production: unknown\n * }\n * const buildConfig = defineEnvironmentConfig<MyEnvs>()({\n * port: { doc: 'Port', format: Number, value: 3000 },\n * })\n * const config = buildConfig('dev')\n * ```\n *\n * @example Fallback environments\n * ```ts\n * const buildConfig = defineEnvironmentConfig<MyEnvs>()(\n * { apiUrl: { doc: 'API URL', format: 'url', staging: 'https://staging' } },\n * { fallbacks: { dev: 'staging' } },\n * )\n * const config = buildConfig('dev') // apiUrl resolved from `staging`\n * ```\n */\nexport function defineEnvironmentConfig<E extends EnvsShape>() {\n const create = createEnvironmentConfig<E>()\n return <G extends ConfigGroup<E>>(\n inputConfig: G,\n options?: CreateConfigOptions<E>,\n ): ((env: EnvName<E>) => ResolveConfigGroup<G> & { env: EnvName<E> }) =>\n (env: EnvName<E>) =>\n create(env, inputConfig, options)\n}\n"],"mappings":";AAEA,SAAgB,kBACd,OACA,QACA,SACA,QACK;CACL,QAAQ,QAAR;EACE,KAAK;GACH,IAAI,OAAO,UAAU,UACnB,OAAO,KAAK,oBAAoB,QAAQ,kBAAkB;GAE5D;EACF,KAAK;GACH,QAAQ,OAAO,KAAK;GACpB,IAAI,MAAM,KAAK,GACb,OAAO,KAAK,oBAAoB,QAAQ,kBAAkB;GAC5D;EACF,KAAK;GACH,IACE,OAAO,UAAU,aACjB,UAAU,UACV,UAAU,WACV,UAAU,KACV,UAAU,GAEV,OAAO,KAAK,oBAAoB,QAAQ,mBAAmB;GAE7D,QACE,UAAU,SAAS,OAAO,UAAU,UAAU,QAAQ,QAAQ,KAAK;GACrE;EACF,KAAK;GACH,IAAI,CAAC,MAAM,QAAQ,KAAK,GACtB,OAAO,KAAK,oBAAoB,QAAQ,kBAAkB;GAE5D;EACF,KAAK;GACH,IAAI;IACF,IAAI,IAAI,KAAK;GACf,QAAQ;IACN,OAAO,KACL,oBAAoB,QAAQ,+BAA+B,MAAM,EACnE;GACF;GACA;EACF,SACE,IAAI,kBAAkB;OAChB,CAAC,OAAO,SAAS,KAAK,GACxB,OAAO,KACL,oBAAoB,QAAQ,oBAAoB,OAAO,KAAK,IAAI,EAAE,EACpE;EAAA;CAGR;CAEA,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACNA,SAAgB,0BAA+C;CAC7D,QACE,KACA,aACA,YAEA,YAAkB,KAAK,aAAa,OAAO;AAC/C;AAEA,SAAS,YACP,KACA,aACA,SAC6C;CAC7C,MAAM,SAAmB,CAAC;CAI1B,MAAM,WAAW,qBAAwB,KAAK,SAAS,SAAS;CAEhE,SAAS,cACP,QACA,WACqB;EACrB,MAAM,SAA8B,CAAC;EAErC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,GAAG;GACjD,IAAI,QAAQ,OACV,MAAM,IAAI,MACR,yFACF;GAGF,MAAM,UAAU,YAAY,GAAG,UAAU,GAAG,QAAQ;GAEpD,IAAI,EAAE,SAAS,QAAQ;IACrB,OAAO,OAAO,cAAc,OAAyB,OAAO;IAC5D;GACF;GAEA,MAAM,cAAc;GAcpB,IAAI,QAAa,WAAW,cAAc,YAAY,QAAQ,KAAA;GAI9D,KAAK,MAAM,gBAAgB,UAAU;IACnC,MAAM,WAAW,YAAY;IAC7B,IAAI,aAAa,KAAA,GAAW;KAC1B,QAAQ;KACR;IACF;GACF;GAGA,IAAI,gBAAgB,aAAa;IAC/B,MAAM,kBAEJ,OAAO,YAAY,eAAe,QAAQ,MAEtC,QAAQ,IAAI,YAAY,cACxB,KAAA;IACN,IAAI,oBAAoB,KAAA,GAAW,QAAQ;GAC7C,OAAO,IAAI,mBAAmB,aAAa;IACzC,MAAM,kBAEJ,OAAO,OAAO,SAAS,eAAe,OAAO,KAAK,MAE9C,OAAO,KAAK,IAAI,YAAY,iBAC5B,KAAA;IACN,IAAI,oBAAoB,KAAA,GAAW,QAAQ;GAC7C;GAEA,MAAM,iBACJ,UAAU,KAAA,KACV,gBAAgB,eAChB,mBAAmB;GAErB,IAAI,UAAU,KAAA,KAAa,CAAC,gBAAgB;IAC1C,OAAO,KACL,gCAAgC,QAAQ,qFAC1C;IACA;GACF;GAEA,IAAI,UAAU,KAAA,GACZ,IAAI,YAAY,UAAU;IACxB,QAAQ,YAAY;IACpB,IAAI,UAAU,KAAA,GAAW;KACvB,OAAO,OAAO,KAAA;KACd;IACF;GACF,OAAO;IACL,OAAO,KACL,qCAAqC,QAAQ,kBAAkB,KACjE;IACA;GACF;GAMF,QAAQ,kBAAkB,OAAO,YAAY,QAAQ,SAAS,MAAM;GAEpE,OAAO,OAAO;EAChB;EAEA,OAAO;CACT;CAEA,MAAM,eAAe,cAAc,aAAa,EAAE;CAElD,IAAI,OAAO,SAAS,GAAG;EACrB,QAAQ,MAAM,wCAAwC,MAAM;EAC5D,MAAM,IAAI,MACR,0CAA0C,OAAO,KAAK,IAAI,GAC5D;CACF;CAEA,aAAa,MAAM;CACnB,OAAO;AACT;;;;;;;AAQA,SAAS,qBACP,KACA,WACc;CACd,MAAM,QAAsB,CAAC,GAAG;CAChC,IAAI,CAAC,WAAW,OAAO;CAEvB,MAAM,OAAO,IAAI,IAAY,CAAC,GAAG,CAAC;CAClC,IAAI,UAAsB;CAC1B,OAAO,UAAU,aAAa,KAAA,GAAW;EACvC,MAAM,OAAO,UAAU;EACvB,IAAI,KAAK,IAAI,IAAI,GACf,MAAM,IAAI,MACR,qCAAqC,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC,KAAK,MAAM,GACnE;EAEF,KAAK,IAAI,IAAI;EACb,MAAM,KAAK,IAAI;EACf,UAAU;CACZ;CACA,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACrLA,SAAgB,0BAA+C;CAC7D,MAAM,SAAS,wBAA2B;CAC1C,QACI,aACA,aAED,QACC,OAAO,KAAK,aAAa,OAAO;AACtC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "konfeeg",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "Build a validated, strongly-typed config object, and use it everywhere.",
|
|
5
|
+
"author": "Zach Olivare <https://github.com/0livare>",
|
|
6
|
+
"license": "ISC",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/0livare/konfeeg"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"main": "dist/index.mjs",
|
|
13
|
+
"module": "dist/index.mjs",
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"prettier": "^3.8.4",
|
|
19
|
+
"tsdown": "^0.22.2",
|
|
20
|
+
"typescript": "^6.0.3",
|
|
21
|
+
"vitest": "^4.1.8"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"environment",
|
|
25
|
+
"config",
|
|
26
|
+
"configuration",
|
|
27
|
+
"typescript",
|
|
28
|
+
"type-safe",
|
|
29
|
+
"validation",
|
|
30
|
+
"browser",
|
|
31
|
+
"node"
|
|
32
|
+
],
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsdown lib/index.ts",
|
|
35
|
+
"test": "vitest --run",
|
|
36
|
+
"types": "tsc",
|
|
37
|
+
"format": "prettier --write .",
|
|
38
|
+
"pr": "tsc && prettier --check . && pnpm run test && pnpm run build"
|
|
39
|
+
}
|
|
40
|
+
}
|