@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 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 { DEFAULT_ENVIRONMENT, DEPLOYED_ENVIRONMENTS, DotenvLoader, ENVIRONMENTS, type EnvLoader, type EnvLookup, type EnvObject, type EnvParser, type EnvParserOptions, type Environment, type Primitive, createEnvLookup, createEnvParser, environment, filterByNamespace, mergeNamespaces };
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
- console.warn(`[@theholocron/utils-env] Unknown environment "${raw}", falling back to "${DEFAULT_ENVIRONMENT}"`);
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
- console.info(`[@theholocron/utils-env] (debugLog) - ${appName} loaded ${keys.length} key(s) from environment`, { keys });
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
- console.warn(`[@theholocron/utils-env] (warnMissingKey) - ${appName} key not found: "${key}" — returned undefined`);
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
- console.warn(`[@theholocron/utils-env] (debugDeprecation) - ${appName} "${oldKey}" is deprecated; migrate to "${newKey}"`);
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.7.0",
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": "^7.24.1",
34
- "@theholocron/vitest-config": "^7.24.1",
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.22.14",
39
+ "tsdown": "^0.23.0",
40
40
  "vitest": "^4.1.10"
41
41
  },
42
42
  "peerDependencies": {