@theholocron/env-utils 1.7.0 → 1.8.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 +8 -0
- package/dist/index.d.mts +28 -12
- package/dist/index.mjs +18 -13
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -38,6 +38,14 @@ export const config = map((get) => ({
|
|
|
38
38
|
}));
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
+
### Logging
|
|
42
|
+
|
|
43
|
+
Debug output (key-loaded counts, missing-key warnings, deprecation notices) goes to `console.info`/`console.warn` by default. Pass an `EnvLogger` (any object shaped `{ info(message, meta?), warn(message, meta?) }`) to route it elsewhere:
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
const { get } = createEnvParser({ appName: "my-app", logger: myLogger });
|
|
47
|
+
```
|
|
48
|
+
|
|
41
49
|
## Documentation
|
|
42
50
|
|
|
43
51
|
Check out [The Holocron Archive](https://docs.theholocron.dev/projects/utils/) for more information.
|
package/dist/index.d.mts
CHANGED
|
@@ -20,7 +20,7 @@ interface EnvLookup {
|
|
|
20
20
|
* return createEnvLookup(source);
|
|
21
21
|
* }
|
|
22
22
|
*/
|
|
23
|
-
declare function createEnvLookup(source?: Record<string, string | undefined>): EnvLookup;
|
|
23
|
+
export declare function createEnvLookup(source?: Record<string, string | undefined>): EnvLookup;
|
|
24
24
|
//#endregion
|
|
25
25
|
//#region src/types.d.ts
|
|
26
26
|
type Primitive = string | number | boolean;
|
|
@@ -34,6 +34,17 @@ type EnvObject = {
|
|
|
34
34
|
interface EnvLoader {
|
|
35
35
|
load(): Record<string, string | undefined>;
|
|
36
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Minimal structural logger seam — accept any object shaped like this
|
|
39
|
+
* (including a real `@theholocron/observability` `Logger` via a thin
|
|
40
|
+
* adapter) without this package taking a dependency on it. Defaults to
|
|
41
|
+
* `console.info`/`console.warn` so behavior is unchanged when nothing
|
|
42
|
+
* is injected.
|
|
43
|
+
*/
|
|
44
|
+
interface EnvLogger {
|
|
45
|
+
info(message: string, meta?: unknown): void;
|
|
46
|
+
warn(message: string, meta?: unknown): void;
|
|
47
|
+
}
|
|
37
48
|
interface EnvParserOptions {
|
|
38
49
|
/**
|
|
39
50
|
* Display name for this app — used in debug output.
|
|
@@ -76,6 +87,11 @@ interface EnvParserOptions {
|
|
|
76
87
|
* createEnvParser({ appName: "my-cli", namespaces: ["MY_TOOL"] })
|
|
77
88
|
*/
|
|
78
89
|
namespaces?: string[];
|
|
90
|
+
/**
|
|
91
|
+
* Structured logger for debug output (key-loaded counts, missing-key
|
|
92
|
+
* warnings, deprecation notices). Defaults to `console.info`/`console.warn`.
|
|
93
|
+
*/
|
|
94
|
+
logger?: EnvLogger;
|
|
79
95
|
}
|
|
80
96
|
interface EnvParser<T extends EnvObject = EnvObject> {
|
|
81
97
|
/**
|
|
@@ -157,12 +173,12 @@ interface EnvParser<T extends EnvObject = EnvObject> {
|
|
|
157
173
|
* auth: get("auth0"), // returns the whole auth0 namespace object
|
|
158
174
|
* }));
|
|
159
175
|
*/
|
|
160
|
-
declare function createEnvParser<T extends EnvObject = EnvObject>(options: EnvParserOptions): EnvParser<T>;
|
|
176
|
+
export declare function createEnvParser<T extends EnvObject = EnvObject>(options: EnvParserOptions): EnvParser<T>;
|
|
161
177
|
//#endregion
|
|
162
178
|
//#region src/environment.d.ts
|
|
163
|
-
declare const ENVIRONMENTS: readonly ["local", "dev", "qa", "prod"];
|
|
164
|
-
declare const DEFAULT_ENVIRONMENT: Environment;
|
|
165
|
-
declare const DEPLOYED_ENVIRONMENTS: Set<"local" | "dev" | "qa" | "prod">;
|
|
179
|
+
export declare const ENVIRONMENTS: readonly ["local", "dev", "qa", "prod"];
|
|
180
|
+
export declare const DEFAULT_ENVIRONMENT: Environment;
|
|
181
|
+
export declare const DEPLOYED_ENVIRONMENTS: Set<"local" | "dev" | "qa" | "prod">;
|
|
166
182
|
/**
|
|
167
183
|
* Normalize a string into a canonical Environment.
|
|
168
184
|
* Returns undefined if the value is not recognized.
|
|
@@ -171,11 +187,11 @@ declare function normalizeEnvironment(value: string): Environment | undefined;
|
|
|
171
187
|
/**
|
|
172
188
|
* Resolve the current environment from input or process.env.
|
|
173
189
|
*/
|
|
174
|
-
declare function getEnvironment(): Environment;
|
|
175
|
-
declare const environment: {
|
|
190
|
+
declare function getEnvironment(logger?: Pick<EnvLogger, "warn">): Environment;
|
|
191
|
+
export declare const environment: {
|
|
176
192
|
get: typeof getEnvironment;
|
|
177
193
|
normalize: typeof normalizeEnvironment;
|
|
178
|
-
isDeployed: () => boolean;
|
|
194
|
+
isDeployed: (logger?: Pick<EnvLogger, "warn">) => boolean;
|
|
179
195
|
};
|
|
180
196
|
type Environment = (typeof ENVIRONMENTS)[number];
|
|
181
197
|
//#endregion
|
|
@@ -187,7 +203,7 @@ type Environment = (typeof ENVIRONMENTS)[number];
|
|
|
187
203
|
* Swap this out via `createEnvParser({ loader: new MyLoader() })`
|
|
188
204
|
* if you ever need Vault, AWS SSM, dotenvx, etc.
|
|
189
205
|
*/
|
|
190
|
-
declare class DotenvLoader implements EnvLoader {
|
|
206
|
+
export declare class DotenvLoader implements EnvLoader {
|
|
191
207
|
load(): Record<string, string | undefined>;
|
|
192
208
|
}
|
|
193
209
|
//#endregion
|
|
@@ -203,7 +219,7 @@ declare class DotenvLoader implements EnvLoader {
|
|
|
203
219
|
* filterByNamespace({ HOLOCRON_DEBUG: "true", PORT: "3000" }, "HOLOCRON")
|
|
204
220
|
* // → { DEBUG: "true" }
|
|
205
221
|
*/
|
|
206
|
-
declare function filterByNamespace(raw: Record<string, string | undefined>, namespace: string): Record<string, string | undefined>;
|
|
222
|
+
export declare function filterByNamespace(raw: Record<string, string | undefined>, namespace: string): Record<string, string | undefined>;
|
|
207
223
|
/**
|
|
208
224
|
* Merge multiple namespace-filtered views of a raw env record.
|
|
209
225
|
*
|
|
@@ -216,6 +232,6 @@ declare function filterByNamespace(raw: Record<string, string | undefined>, name
|
|
|
216
232
|
* // HOLOCRON_DEBUG is the org-wide default; CLI_TEMPLATE_DEBUG overrides it.
|
|
217
233
|
* mergeNamespaces(raw, ["HOLOCRON", "CLI_TEMPLATE"])
|
|
218
234
|
*/
|
|
219
|
-
declare function mergeNamespaces(raw: Record<string, string | undefined>, namespaces: string[]): Record<string, string | undefined>;
|
|
235
|
+
export declare function mergeNamespaces(raw: Record<string, string | undefined>, namespaces: string[]): Record<string, string | undefined>;
|
|
220
236
|
//#endregion
|
|
221
|
-
export {
|
|
237
|
+
export type { EnvLoader, EnvLogger, EnvLookup, EnvObject, EnvParser, EnvParserOptions, Environment, Primitive };
|
package/dist/index.mjs
CHANGED
|
@@ -48,14 +48,15 @@ function normalizeEnvironment(value) {
|
|
|
48
48
|
production: "prod"
|
|
49
49
|
})).get(value.toLowerCase());
|
|
50
50
|
}
|
|
51
|
+
const consoleWarn = { warn: (message) => console.warn(message) };
|
|
51
52
|
/**
|
|
52
53
|
* Resolve the current environment from input or process.env.
|
|
53
54
|
*/
|
|
54
|
-
function getEnvironment() {
|
|
55
|
+
function getEnvironment(logger = consoleWarn) {
|
|
55
56
|
const raw = process.env["ENVIRONMENT"] || process.env["ENV"] || process.env["NODE_ENV"] || "prod";
|
|
56
57
|
const normalized = normalizeEnvironment(String(raw));
|
|
57
58
|
if (!normalized) {
|
|
58
|
-
|
|
59
|
+
logger.warn(`[@theholocron/utils-env] Unknown environment "${raw}", falling back to "${DEFAULT_ENVIRONMENT}"`);
|
|
59
60
|
return DEFAULT_ENVIRONMENT;
|
|
60
61
|
}
|
|
61
62
|
return normalized;
|
|
@@ -63,7 +64,7 @@ function getEnvironment() {
|
|
|
63
64
|
const environment = {
|
|
64
65
|
get: getEnvironment,
|
|
65
66
|
normalize: normalizeEnvironment,
|
|
66
|
-
isDeployed: () => DEPLOYED_ENVIRONMENTS.has(getEnvironment())
|
|
67
|
+
isDeployed: (logger) => DEPLOYED_ENVIRONMENTS.has(getEnvironment(logger))
|
|
67
68
|
};
|
|
68
69
|
//#endregion
|
|
69
70
|
//#region src/utils/coerce-value.ts
|
|
@@ -216,6 +217,10 @@ function normalizeKey(key) {
|
|
|
216
217
|
}
|
|
217
218
|
//#endregion
|
|
218
219
|
//#region src/debug.ts
|
|
220
|
+
const consoleLogger = {
|
|
221
|
+
info: (message, meta) => meta === void 0 ? console.info(message) : console.info(message, meta),
|
|
222
|
+
warn: (message, meta) => meta === void 0 ? console.warn(message) : console.warn(message, meta)
|
|
223
|
+
};
|
|
219
224
|
function isDebugEnabled() {
|
|
220
225
|
const raw = process.env["DEBUG"];
|
|
221
226
|
if (!raw) return false;
|
|
@@ -225,21 +230,21 @@ function isDebugEnabled() {
|
|
|
225
230
|
"yes"
|
|
226
231
|
].includes(raw.toLowerCase());
|
|
227
232
|
}
|
|
228
|
-
function debugLog(appName, env) {
|
|
233
|
+
function debugLog(appName, env, logger = consoleLogger) {
|
|
229
234
|
if (environment.isDeployed()) return;
|
|
230
235
|
if (!isDebugEnabled()) return;
|
|
231
236
|
const keys = collectKeys(env);
|
|
232
|
-
|
|
237
|
+
logger.info(`[@theholocron/utils-env] (debugLog) - ${appName} loaded ${keys.length} key(s) from environment`, { keys });
|
|
233
238
|
}
|
|
234
|
-
function warnMissingKey(appName, key) {
|
|
239
|
+
function warnMissingKey(appName, key, logger = consoleLogger) {
|
|
235
240
|
if (environment.isDeployed()) return;
|
|
236
241
|
if (!isDebugEnabled()) return;
|
|
237
|
-
|
|
242
|
+
logger.warn(`[@theholocron/utils-env] (warnMissingKey) - ${appName} key not found: "${key}" — returned undefined`);
|
|
238
243
|
}
|
|
239
|
-
function debugDeprecation(appName, oldKey, newKey, env) {
|
|
244
|
+
function debugDeprecation(appName, oldKey, newKey, env, logger = consoleLogger) {
|
|
240
245
|
if (environment.isDeployed()) return;
|
|
241
246
|
if (!(oldKey in env)) return;
|
|
242
|
-
|
|
247
|
+
logger.warn(`[@theholocron/utils-env] (debugDeprecation) - ${appName} "${oldKey}" is deprecated; migrate to "${newKey}"`);
|
|
243
248
|
}
|
|
244
249
|
const debug = {
|
|
245
250
|
log: debugLog,
|
|
@@ -286,14 +291,14 @@ var DotenvLoader = class {
|
|
|
286
291
|
* }));
|
|
287
292
|
*/
|
|
288
293
|
function createEnvParser(options) {
|
|
289
|
-
const { appName, loader = new DotenvLoader(), parseValues = true, namespaces } = options;
|
|
294
|
+
const { appName, loader = new DotenvLoader(), parseValues = true, namespaces, logger } = options;
|
|
290
295
|
const loaded = loader.load();
|
|
291
296
|
const env = buildEnvObject(namespaces && namespaces.length > 0 ? mergeNamespaces(loaded, namespaces) : loaded, parseValues);
|
|
292
|
-
debug.log(appName, env);
|
|
297
|
+
debug.log(appName, env, logger);
|
|
293
298
|
function get(key) {
|
|
294
299
|
const path = normalizeKey(key);
|
|
295
300
|
const value = getByPath(env, path);
|
|
296
|
-
if (value === void 0) debug.warn(appName, key);
|
|
301
|
+
if (value === void 0) debug.warn(appName, key, logger);
|
|
297
302
|
return value;
|
|
298
303
|
}
|
|
299
304
|
return {
|
|
@@ -305,7 +310,7 @@ function createEnvParser(options) {
|
|
|
305
310
|
return fn(get);
|
|
306
311
|
},
|
|
307
312
|
deprecate(oldKey, newKey) {
|
|
308
|
-
debug.deprecate(appName, oldKey, newKey, env);
|
|
313
|
+
debug.deprecate(appName, oldKey, newKey, env, logger);
|
|
309
314
|
}
|
|
310
315
|
};
|
|
311
316
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theholocron/env-utils",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "Runtime environment detection and typed env-var parsing.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"env",
|
|
@@ -30,13 +30,13 @@
|
|
|
30
30
|
"dist"
|
|
31
31
|
],
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@theholocron/tsdown-config": "^
|
|
34
|
-
"@theholocron/vitest-config": "^
|
|
33
|
+
"@theholocron/tsdown-config": "^8.2.0",
|
|
34
|
+
"@theholocron/vitest-config": "^8.2.0",
|
|
35
35
|
"@tsconfig/node-lts": "^24.0.1",
|
|
36
36
|
"@types/node": "^26.2.0",
|
|
37
37
|
"@vitest/coverage-v8": "^4.1.10",
|
|
38
38
|
"dotenv": "^17.4.2",
|
|
39
|
-
"tsdown": "^0.
|
|
39
|
+
"tsdown": "^0.23.0",
|
|
40
40
|
"vitest": "^4.1.10"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|