@theholocron/env-utils 1.13.0 → 1.15.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 +28 -0
- package/dist/index.d.mts +2 -24
- package/dist/index.mjs +2 -25
- package/dist/lookup.d.mts +2 -0
- package/dist/lookup.mjs +2 -0
- package/dist/with-env-prefix-BtxbH3HN.d.mts +57 -0
- package/dist/with-env-prefix-CC-WUj7Q.mjs +59 -0
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -38,6 +38,34 @@ export const config = map((get) => ({
|
|
|
38
38
|
}));
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
+
### Precedence chains
|
|
42
|
+
|
|
43
|
+
`firstEnv(env, names, fallback?)` returns the first variable in `names` that is set and non-empty, else `fallback`:
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import { createEnvLookup, firstEnv } from "@theholocron/env-utils";
|
|
47
|
+
|
|
48
|
+
const env = createEnvLookup();
|
|
49
|
+
const dsn = firstEnv(env, ["MYAPP_SENTRY_DSN", "SENTRY_DSN"], DEFAULT_DSN);
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`withEnvPrefix(env, prefix)` wraps an `EnvLookup` so `get("KEY")` tries `<prefix>_KEY` first, then `KEY`. The prefix is matched with exact case, so pass it in upper case. Name the variable once and let the namespaced override win:
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import { createEnvLookup, withEnvPrefix } from "@theholocron/env-utils";
|
|
56
|
+
|
|
57
|
+
const env = withEnvPrefix(createEnvLookup(), "MYAPP");
|
|
58
|
+
env.get("SENTRY_DSN"); // MYAPP_SENTRY_DSN, else SENTRY_DSN
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Importing without side effects
|
|
62
|
+
|
|
63
|
+
The package entry also exports `DotenvLoader`, whose module runs `import "dotenv/config"` and so loads `.env` into `process.env` the moment the package is imported. Libraries that only read variables should import from the `/lookup` subpath, which has `createEnvLookup`, `firstEnv` and `withEnvPrefix` and never touches dotenv:
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
import { createEnvLookup, firstEnv } from "@theholocron/env-utils/lookup";
|
|
67
|
+
```
|
|
68
|
+
|
|
41
69
|
### Logging
|
|
42
70
|
|
|
43
71
|
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:
|
package/dist/index.d.mts
CHANGED
|
@@ -1,27 +1,5 @@
|
|
|
1
|
+
import { i as createEnvLookup, n as firstEnv, r as EnvLookup, t as withEnvPrefix } from "./with-env-prefix-BtxbH3HN.mjs";
|
|
1
2
|
import "dotenv/config";
|
|
2
|
-
//#region src/create-env-lookup.d.ts
|
|
3
|
-
interface EnvLookup {
|
|
4
|
-
/** Return the string value for `key`, or `undefined` if absent or empty. */
|
|
5
|
-
get(key: string): string | undefined;
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* Create a lightweight env lookup backed by a fixed source dict or, when
|
|
9
|
-
* called with no arguments, by a lazy read of `process.env` on every call.
|
|
10
|
-
*
|
|
11
|
-
* Use the no-arg form for the application singleton — it always reflects the
|
|
12
|
-
* current state of `process.env` so tests can mutate it freely:
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* // singleton — reads process.env lazily
|
|
16
|
-
* export const env = createEnvLookup();
|
|
17
|
-
*
|
|
18
|
-
* // injectable — tests pass a fake dict
|
|
19
|
-
* export function makeEnv(source?: Record<string, string | undefined>) {
|
|
20
|
-
* return createEnvLookup(source);
|
|
21
|
-
* }
|
|
22
|
-
*/
|
|
23
|
-
export declare function createEnvLookup(source?: Record<string, string | undefined>): EnvLookup;
|
|
24
|
-
//#endregion
|
|
25
3
|
//#region src/types.d.ts
|
|
26
4
|
type Primitive = string | number | boolean;
|
|
27
5
|
type EnvObject = {
|
|
@@ -234,4 +212,4 @@ export declare function filterByNamespace(raw: Record<string, string | undefined
|
|
|
234
212
|
*/
|
|
235
213
|
export declare function mergeNamespaces(raw: Record<string, string | undefined>, namespaces: string[]): Record<string, string | undefined>;
|
|
236
214
|
//#endregion
|
|
237
|
-
export type
|
|
215
|
+
export { type EnvLoader, type EnvLogger, type EnvLookup, type EnvObject, type EnvParser, type EnvParserOptions, type Environment, type Primitive, createEnvLookup, firstEnv, withEnvPrefix };
|
package/dist/index.mjs
CHANGED
|
@@ -1,28 +1,5 @@
|
|
|
1
|
+
import { n as firstEnv, r as createEnvLookup, t as withEnvPrefix } from "./with-env-prefix-CC-WUj7Q.mjs";
|
|
1
2
|
import "dotenv/config";
|
|
2
|
-
//#region src/create-env-lookup.ts
|
|
3
|
-
/**
|
|
4
|
-
* Create a lightweight env lookup backed by a fixed source dict or, when
|
|
5
|
-
* called with no arguments, by a lazy read of `process.env` on every call.
|
|
6
|
-
*
|
|
7
|
-
* Use the no-arg form for the application singleton — it always reflects the
|
|
8
|
-
* current state of `process.env` so tests can mutate it freely:
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* // singleton — reads process.env lazily
|
|
12
|
-
* export const env = createEnvLookup();
|
|
13
|
-
*
|
|
14
|
-
* // injectable — tests pass a fake dict
|
|
15
|
-
* export function makeEnv(source?: Record<string, string | undefined>) {
|
|
16
|
-
* return createEnvLookup(source);
|
|
17
|
-
* }
|
|
18
|
-
*/
|
|
19
|
-
function createEnvLookup(source) {
|
|
20
|
-
return { get(key) {
|
|
21
|
-
const val = (source ?? process.env)[key];
|
|
22
|
-
return typeof val === "string" && val ? val : void 0;
|
|
23
|
-
} };
|
|
24
|
-
}
|
|
25
|
-
//#endregion
|
|
26
3
|
//#region src/environment.ts
|
|
27
4
|
const ENVIRONMENTS = [
|
|
28
5
|
"local",
|
|
@@ -315,4 +292,4 @@ function createEnvParser(options) {
|
|
|
315
292
|
};
|
|
316
293
|
}
|
|
317
294
|
//#endregion
|
|
318
|
-
export { DEFAULT_ENVIRONMENT, DEPLOYED_ENVIRONMENTS, DotenvLoader, ENVIRONMENTS, createEnvLookup, createEnvParser, environment, filterByNamespace, mergeNamespaces };
|
|
295
|
+
export { DEFAULT_ENVIRONMENT, DEPLOYED_ENVIRONMENTS, DotenvLoader, ENVIRONMENTS, createEnvLookup, createEnvParser, environment, filterByNamespace, firstEnv, mergeNamespaces, withEnvPrefix };
|
package/dist/lookup.mjs
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
//#region src/create-env-lookup.d.ts
|
|
2
|
+
interface EnvLookup {
|
|
3
|
+
/** Return the string value for `key`, or `undefined` if absent or empty. */
|
|
4
|
+
get(key: string): string | undefined;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Create a lightweight env lookup backed by a fixed source dict or, when
|
|
8
|
+
* called with no arguments, by a lazy read of `process.env` on every call.
|
|
9
|
+
*
|
|
10
|
+
* Use the no-arg form for the application singleton — it always reflects the
|
|
11
|
+
* current state of `process.env` so tests can mutate it freely:
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* // singleton — reads process.env lazily
|
|
15
|
+
* export const env = createEnvLookup();
|
|
16
|
+
*
|
|
17
|
+
* // injectable — tests pass a fake dict
|
|
18
|
+
* export function makeEnv(source?: Record<string, string | undefined>) {
|
|
19
|
+
* return createEnvLookup(source);
|
|
20
|
+
* }
|
|
21
|
+
*/
|
|
22
|
+
declare function createEnvLookup(source?: Record<string, string | undefined>): EnvLookup;
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/first-env.d.ts
|
|
25
|
+
/**
|
|
26
|
+
* Return the value of the first variable in `names` that is set and
|
|
27
|
+
* non-empty, or `fallback` when none is. Use it for a precedence chain such
|
|
28
|
+
* as a product-specific name, then the vendor-native name, then a default.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* const dsn = firstEnv(env, ["HOLOCRON_SENTRY_DSN", "SENTRY_DSN"], DEFAULT_DSN);
|
|
32
|
+
*/
|
|
33
|
+
declare function firstEnv(env: EnvLookup, names: readonly string[], fallback: string): string;
|
|
34
|
+
declare function firstEnv(env: EnvLookup, names: readonly string[], fallback?: string): string | undefined;
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/with-env-prefix.d.ts
|
|
37
|
+
/**
|
|
38
|
+
* Wrap an {@link EnvLookup} so every `get(key)` tries `<prefix>_<key>` first
|
|
39
|
+
* and falls back to the bare `key`. Callers name the variable once, and the
|
|
40
|
+
* namespaced override wins when it is set.
|
|
41
|
+
*
|
|
42
|
+
* Pass the prefix without a trailing underscore. Empty values are skipped,
|
|
43
|
+
* as in {@link EnvLookup.get}.
|
|
44
|
+
*
|
|
45
|
+
* The prefix is matched with exact case: `"holocron"` looks up
|
|
46
|
+
* `holocron_TOKEN`, not `HOLOCRON_TOKEN`. An {@link EnvLookup} only answers
|
|
47
|
+
* exact-name `get` calls, so unlike `filterByNamespace` (which scans a whole
|
|
48
|
+
* env record) this cannot match case-insensitively. Environment variable
|
|
49
|
+
* names are conventionally upper case, so pass an upper-case prefix.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* const env = withEnvPrefix(createEnvLookup(), "HOLOCRON");
|
|
53
|
+
* env.get("SENTRY_DSN"); // HOLOCRON_SENTRY_DSN, else SENTRY_DSN
|
|
54
|
+
*/
|
|
55
|
+
declare function withEnvPrefix(env: EnvLookup, prefix: string): EnvLookup;
|
|
56
|
+
//#endregion
|
|
57
|
+
export { createEnvLookup as i, firstEnv as n, EnvLookup as r, withEnvPrefix as t };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
//#region src/create-env-lookup.ts
|
|
2
|
+
/**
|
|
3
|
+
* Create a lightweight env lookup backed by a fixed source dict or, when
|
|
4
|
+
* called with no arguments, by a lazy read of `process.env` on every call.
|
|
5
|
+
*
|
|
6
|
+
* Use the no-arg form for the application singleton — it always reflects the
|
|
7
|
+
* current state of `process.env` so tests can mutate it freely:
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* // singleton — reads process.env lazily
|
|
11
|
+
* export const env = createEnvLookup();
|
|
12
|
+
*
|
|
13
|
+
* // injectable — tests pass a fake dict
|
|
14
|
+
* export function makeEnv(source?: Record<string, string | undefined>) {
|
|
15
|
+
* return createEnvLookup(source);
|
|
16
|
+
* }
|
|
17
|
+
*/
|
|
18
|
+
function createEnvLookup(source) {
|
|
19
|
+
return { get(key) {
|
|
20
|
+
const val = (source ?? process.env)[key];
|
|
21
|
+
return typeof val === "string" && val ? val : void 0;
|
|
22
|
+
} };
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/first-env.ts
|
|
26
|
+
function firstEnv(env, names, fallback) {
|
|
27
|
+
for (const name of names) {
|
|
28
|
+
const value = env.get(name);
|
|
29
|
+
if (value !== void 0) return value;
|
|
30
|
+
}
|
|
31
|
+
return fallback;
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
34
|
+
//#region src/with-env-prefix.ts
|
|
35
|
+
/**
|
|
36
|
+
* Wrap an {@link EnvLookup} so every `get(key)` tries `<prefix>_<key>` first
|
|
37
|
+
* and falls back to the bare `key`. Callers name the variable once, and the
|
|
38
|
+
* namespaced override wins when it is set.
|
|
39
|
+
*
|
|
40
|
+
* Pass the prefix without a trailing underscore. Empty values are skipped,
|
|
41
|
+
* as in {@link EnvLookup.get}.
|
|
42
|
+
*
|
|
43
|
+
* The prefix is matched with exact case: `"holocron"` looks up
|
|
44
|
+
* `holocron_TOKEN`, not `HOLOCRON_TOKEN`. An {@link EnvLookup} only answers
|
|
45
|
+
* exact-name `get` calls, so unlike `filterByNamespace` (which scans a whole
|
|
46
|
+
* env record) this cannot match case-insensitively. Environment variable
|
|
47
|
+
* names are conventionally upper case, so pass an upper-case prefix.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* const env = withEnvPrefix(createEnvLookup(), "HOLOCRON");
|
|
51
|
+
* env.get("SENTRY_DSN"); // HOLOCRON_SENTRY_DSN, else SENTRY_DSN
|
|
52
|
+
*/
|
|
53
|
+
function withEnvPrefix(env, prefix) {
|
|
54
|
+
return { get(key) {
|
|
55
|
+
return env.get(`${prefix}_${key}`) ?? env.get(key);
|
|
56
|
+
} };
|
|
57
|
+
}
|
|
58
|
+
//#endregion
|
|
59
|
+
export { firstEnv as n, createEnvLookup as r, withEnvPrefix as t };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theholocron/env-utils",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.0",
|
|
4
4
|
"description": "Runtime environment detection and typed env-var parsing.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"env",
|
|
@@ -24,6 +24,11 @@
|
|
|
24
24
|
"types": "./dist/index.d.mts",
|
|
25
25
|
"import": "./dist/index.mjs",
|
|
26
26
|
"default": "./dist/index.mjs"
|
|
27
|
+
},
|
|
28
|
+
"./lookup": {
|
|
29
|
+
"types": "./dist/lookup.d.mts",
|
|
30
|
+
"import": "./dist/lookup.mjs",
|
|
31
|
+
"default": "./dist/lookup.mjs"
|
|
27
32
|
}
|
|
28
33
|
},
|
|
29
34
|
"files": [
|