@zodal/dials-store-env 0.1.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 +29 -0
- package/dist/index.cjs +86 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +39 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.js +59 -0
- package/dist/index.js.map +1 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# @zodal/dials-store-env
|
|
2
|
+
|
|
3
|
+
Environment-variable [`LayerStore`](https://github.com/i2mint/zodal-dials) for **zodal-dials** — a high-precedence, **read-only** scope.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm i @zodal/dials-store-env @zodal/dials-core
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { createEnvStore } from '@zodal/dials-store-env';
|
|
11
|
+
|
|
12
|
+
const env = createEnvStore({
|
|
13
|
+
prefix: 'MYAPP',
|
|
14
|
+
keys: ['editor.fontSize', 'flags.beta'], // env is key-driven
|
|
15
|
+
});
|
|
16
|
+
// process.env.MYAPP_EDITOR__FONT_SIZE = "18", MYAPP_FLAGS__BETA = "true"
|
|
17
|
+
await env.load(); // → { 'editor.fontSize': 18, 'flags.beta': true }
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
| Capability | |
|
|
21
|
+
|---|---|
|
|
22
|
+
| readable | ✅ |
|
|
23
|
+
| writable | ❌ (read-only) |
|
|
24
|
+
| watchable | ❌ |
|
|
25
|
+
|
|
26
|
+
- **Key → env var**: `editor.fontSize` (prefix `MYAPP`) → `MYAPP_EDITOR__FONT_SIZE` (`.`/`-`/`/` → `__`, camelCase → `_`, uppercased). The mapping is **not invertible**; the store throws at construction if two keys collide on one var.
|
|
27
|
+
- **Coercion** (`defaultCoerce`) is **lossy-safe**: `true`/`false`, round-trippable numbers, and JSON objects/arrays are parsed; leading-zero / big-int / signed-zero strings are kept as strings. Override per use via `coerce`.
|
|
28
|
+
|
|
29
|
+
Part of the [zodal-dials](https://github.com/i2mint/zodal-dials) ecosystem.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
createEnvStore: () => createEnvStore,
|
|
24
|
+
defaultCoerce: () => defaultCoerce,
|
|
25
|
+
envVarName: () => envVarName
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(index_exports);
|
|
28
|
+
function envVarName(key, prefix = "") {
|
|
29
|
+
const body = key.replace(/[.\-/]+/g, "__").replace(/([a-z0-9])([A-Z])/g, "$1_$2").toUpperCase();
|
|
30
|
+
return prefix ? `${prefix.replace(/_+$/, "")}_${body}` : body;
|
|
31
|
+
}
|
|
32
|
+
function defaultCoerce(raw) {
|
|
33
|
+
const trimmed = raw.trim();
|
|
34
|
+
if (trimmed === "true") return true;
|
|
35
|
+
if (trimmed === "false") return false;
|
|
36
|
+
if (/^-?\d+(?:\.\d+)?$/.test(trimmed)) {
|
|
37
|
+
const n = Number(trimmed);
|
|
38
|
+
return String(n) === trimmed ? n : raw;
|
|
39
|
+
}
|
|
40
|
+
if (trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
41
|
+
try {
|
|
42
|
+
return JSON.parse(trimmed);
|
|
43
|
+
} catch {
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return raw;
|
|
47
|
+
}
|
|
48
|
+
function ambientEnv() {
|
|
49
|
+
const proc = globalThis.process;
|
|
50
|
+
return proc?.env ?? {};
|
|
51
|
+
}
|
|
52
|
+
function createEnvStore(options = {}) {
|
|
53
|
+
const scope = options.scope ?? "env";
|
|
54
|
+
const prefix = options.prefix ?? "";
|
|
55
|
+
const keys = options.keys ?? [];
|
|
56
|
+
const coerce = options.coerce ?? defaultCoerce;
|
|
57
|
+
const claimed = /* @__PURE__ */ new Map();
|
|
58
|
+
for (const key of keys) {
|
|
59
|
+
const name = envVarName(key, prefix);
|
|
60
|
+
const prior = claimed.get(name);
|
|
61
|
+
if (prior !== void 0 && prior !== key) {
|
|
62
|
+
throw new Error(`@zodal/dials-store-env: keys "${prior}" and "${key}" both map to env var ${name}`);
|
|
63
|
+
}
|
|
64
|
+
claimed.set(name, key);
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
scope,
|
|
68
|
+
getCapabilities: () => ({ readable: true, writable: false, watchable: false }),
|
|
69
|
+
load() {
|
|
70
|
+
const env = options.env ?? ambientEnv();
|
|
71
|
+
const layer = {};
|
|
72
|
+
for (const key of keys) {
|
|
73
|
+
const raw = env[envVarName(key, prefix)];
|
|
74
|
+
if (raw !== void 0) layer[key] = coerce(raw, key);
|
|
75
|
+
}
|
|
76
|
+
return Promise.resolve(layer);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
81
|
+
0 && (module.exports = {
|
|
82
|
+
createEnvStore,
|
|
83
|
+
defaultCoerce,
|
|
84
|
+
envVarName
|
|
85
|
+
});
|
|
86
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @zodal/dials-store-env — an environment-variable `LayerStore` for zodal-dials.\n *\n * Env is a high-precedence, READ-MOSTLY scope: it produces a layer from `process.env` (or any\n * injected env map) using one deterministic dotted-key -> ENV_VAR mapping, coercing string values to\n * settings values. It is key-driven (you supply the setting keys to look for) and never writes.\n */\n\nimport type { Layer, LayerStore, LayerStoreCapabilities } from '@zodal/dials-core';\n\nexport interface EnvStoreOptions {\n /** Scope id. Default: 'env'. */\n scope?: string;\n /** The setting keys to read (their env var names are derived). Env is key-driven — supply the keys\n * you expect to be overridable via the environment. */\n keys?: string[];\n /** Env var prefix (e.g. 'MYAPP' -> `MYAPP_…`). Default: '' (no prefix). */\n prefix?: string;\n /** The environment to read. Default: the ambient `process.env` (or `{}` in a non-Node runtime). */\n env?: Record<string, string | undefined>;\n /** Coerce a raw string env value to a setting value. Default: `defaultCoerce`. */\n coerce?: (raw: string, key: string) => unknown;\n}\n\n/**\n * Map a dotted setting key to its env var name: PREFIX + the path with `.`/`-`/`/` -> `__` and\n * camelCase -> `_`, uppercased. e.g. `editor.fontSize` (prefix `MYAPP`) -> `MYAPP_EDITOR__FONT_SIZE`.\n */\nexport function envVarName(key: string, prefix = ''): string {\n const body = key\n .replace(/[.\\-/]+/g, '__')\n .replace(/([a-z0-9])([A-Z])/g, '$1_$2')\n .toUpperCase();\n return prefix ? `${prefix.replace(/_+$/, '')}_${body}` : body;\n}\n\n/**\n * Coerce a raw env string: booleans, numbers, and JSON objects/arrays are parsed; else the string.\n * A numeric-looking string is only coerced when it ROUND-TRIPS exactly (`String(Number(x)) === x`),\n * so leading-zero values (\"007\"), big integers beyond MAX_SAFE_INTEGER, signed zero, etc. are kept\n * as strings rather than silently corrupted.\n */\nexport function defaultCoerce(raw: string): unknown {\n const trimmed = raw.trim();\n if (trimmed === 'true') return true;\n if (trimmed === 'false') return false;\n if (/^-?\\d+(?:\\.\\d+)?$/.test(trimmed)) {\n const n = Number(trimmed);\n return String(n) === trimmed ? n : raw;\n }\n if ((trimmed.startsWith('{') && trimmed.endsWith('}')) || (trimmed.startsWith('[') && trimmed.endsWith(']'))) {\n try {\n return JSON.parse(trimmed);\n } catch {\n // not valid JSON — fall through to the raw string\n }\n }\n return raw;\n}\n\nfunction ambientEnv(): Record<string, string | undefined> {\n const proc = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process;\n return proc?.env ?? {};\n}\n\n/** Create a read-only environment-variable LayerStore. */\nexport function createEnvStore(options: EnvStoreOptions = {}): LayerStore {\n const scope = options.scope ?? 'env';\n const prefix = options.prefix ?? '';\n const keys = options.keys ?? [];\n const coerce = options.coerce ?? defaultCoerce;\n\n // The key -> env-var mapping is not injective (e.g. `a.b`, `a-b`, `a/b` all map to `A__B`). Detect\n // collisions at construction so two distinct keys can never silently read the same variable.\n const claimed = new Map<string, string>();\n for (const key of keys) {\n const name = envVarName(key, prefix);\n const prior = claimed.get(name);\n if (prior !== undefined && prior !== key) {\n throw new Error(`@zodal/dials-store-env: keys \"${prior}\" and \"${key}\" both map to env var ${name}`);\n }\n claimed.set(name, key);\n }\n\n return {\n scope,\n getCapabilities: (): LayerStoreCapabilities => ({ readable: true, writable: false, watchable: false }),\n load(): Promise<Layer> {\n const env = options.env ?? ambientEnv();\n const layer: Layer = {};\n for (const key of keys) {\n const raw = env[envVarName(key, prefix)];\n if (raw !== undefined) layer[key] = coerce(raw, key);\n }\n return Promise.resolve(layer);\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4BO,SAAS,WAAW,KAAa,SAAS,IAAY;AAC3D,QAAM,OAAO,IACV,QAAQ,YAAY,IAAI,EACxB,QAAQ,sBAAsB,OAAO,EACrC,YAAY;AACf,SAAO,SAAS,GAAG,OAAO,QAAQ,OAAO,EAAE,CAAC,IAAI,IAAI,KAAK;AAC3D;AAQO,SAAS,cAAc,KAAsB;AAClD,QAAM,UAAU,IAAI,KAAK;AACzB,MAAI,YAAY,OAAQ,QAAO;AAC/B,MAAI,YAAY,QAAS,QAAO;AAChC,MAAI,oBAAoB,KAAK,OAAO,GAAG;AACrC,UAAM,IAAI,OAAO,OAAO;AACxB,WAAO,OAAO,CAAC,MAAM,UAAU,IAAI;AAAA,EACrC;AACA,MAAK,QAAQ,WAAW,GAAG,KAAK,QAAQ,SAAS,GAAG,KAAO,QAAQ,WAAW,GAAG,KAAK,QAAQ,SAAS,GAAG,GAAI;AAC5G,QAAI;AACF,aAAO,KAAK,MAAM,OAAO;AAAA,IAC3B,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,aAAiD;AACxD,QAAM,OAAQ,WAA0E;AACxF,SAAO,MAAM,OAAO,CAAC;AACvB;AAGO,SAAS,eAAe,UAA2B,CAAC,GAAe;AACxE,QAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,OAAO,QAAQ,QAAQ,CAAC;AAC9B,QAAM,SAAS,QAAQ,UAAU;AAIjC,QAAM,UAAU,oBAAI,IAAoB;AACxC,aAAW,OAAO,MAAM;AACtB,UAAM,OAAO,WAAW,KAAK,MAAM;AACnC,UAAM,QAAQ,QAAQ,IAAI,IAAI;AAC9B,QAAI,UAAU,UAAa,UAAU,KAAK;AACxC,YAAM,IAAI,MAAM,iCAAiC,KAAK,UAAU,GAAG,yBAAyB,IAAI,EAAE;AAAA,IACpG;AACA,YAAQ,IAAI,MAAM,GAAG;AAAA,EACvB;AAEA,SAAO;AAAA,IACL;AAAA,IACA,iBAAiB,OAA+B,EAAE,UAAU,MAAM,UAAU,OAAO,WAAW,MAAM;AAAA,IACpG,OAAuB;AACrB,YAAM,MAAM,QAAQ,OAAO,WAAW;AACtC,YAAM,QAAe,CAAC;AACtB,iBAAW,OAAO,MAAM;AACtB,cAAM,MAAM,IAAI,WAAW,KAAK,MAAM,CAAC;AACvC,YAAI,QAAQ,OAAW,OAAM,GAAG,IAAI,OAAO,KAAK,GAAG;AAAA,MACrD;AACA,aAAO,QAAQ,QAAQ,KAAK;AAAA,IAC9B;AAAA,EACF;AACF;","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { LayerStore } from '@zodal/dials-core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @zodal/dials-store-env — an environment-variable `LayerStore` for zodal-dials.
|
|
5
|
+
*
|
|
6
|
+
* Env is a high-precedence, READ-MOSTLY scope: it produces a layer from `process.env` (or any
|
|
7
|
+
* injected env map) using one deterministic dotted-key -> ENV_VAR mapping, coercing string values to
|
|
8
|
+
* settings values. It is key-driven (you supply the setting keys to look for) and never writes.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
interface EnvStoreOptions {
|
|
12
|
+
/** Scope id. Default: 'env'. */
|
|
13
|
+
scope?: string;
|
|
14
|
+
/** The setting keys to read (their env var names are derived). Env is key-driven — supply the keys
|
|
15
|
+
* you expect to be overridable via the environment. */
|
|
16
|
+
keys?: string[];
|
|
17
|
+
/** Env var prefix (e.g. 'MYAPP' -> `MYAPP_…`). Default: '' (no prefix). */
|
|
18
|
+
prefix?: string;
|
|
19
|
+
/** The environment to read. Default: the ambient `process.env` (or `{}` in a non-Node runtime). */
|
|
20
|
+
env?: Record<string, string | undefined>;
|
|
21
|
+
/** Coerce a raw string env value to a setting value. Default: `defaultCoerce`. */
|
|
22
|
+
coerce?: (raw: string, key: string) => unknown;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Map a dotted setting key to its env var name: PREFIX + the path with `.`/`-`/`/` -> `__` and
|
|
26
|
+
* camelCase -> `_`, uppercased. e.g. `editor.fontSize` (prefix `MYAPP`) -> `MYAPP_EDITOR__FONT_SIZE`.
|
|
27
|
+
*/
|
|
28
|
+
declare function envVarName(key: string, prefix?: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Coerce a raw env string: booleans, numbers, and JSON objects/arrays are parsed; else the string.
|
|
31
|
+
* A numeric-looking string is only coerced when it ROUND-TRIPS exactly (`String(Number(x)) === x`),
|
|
32
|
+
* so leading-zero values ("007"), big integers beyond MAX_SAFE_INTEGER, signed zero, etc. are kept
|
|
33
|
+
* as strings rather than silently corrupted.
|
|
34
|
+
*/
|
|
35
|
+
declare function defaultCoerce(raw: string): unknown;
|
|
36
|
+
/** Create a read-only environment-variable LayerStore. */
|
|
37
|
+
declare function createEnvStore(options?: EnvStoreOptions): LayerStore;
|
|
38
|
+
|
|
39
|
+
export { type EnvStoreOptions, createEnvStore, defaultCoerce, envVarName };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { LayerStore } from '@zodal/dials-core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @zodal/dials-store-env — an environment-variable `LayerStore` for zodal-dials.
|
|
5
|
+
*
|
|
6
|
+
* Env is a high-precedence, READ-MOSTLY scope: it produces a layer from `process.env` (or any
|
|
7
|
+
* injected env map) using one deterministic dotted-key -> ENV_VAR mapping, coercing string values to
|
|
8
|
+
* settings values. It is key-driven (you supply the setting keys to look for) and never writes.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
interface EnvStoreOptions {
|
|
12
|
+
/** Scope id. Default: 'env'. */
|
|
13
|
+
scope?: string;
|
|
14
|
+
/** The setting keys to read (their env var names are derived). Env is key-driven — supply the keys
|
|
15
|
+
* you expect to be overridable via the environment. */
|
|
16
|
+
keys?: string[];
|
|
17
|
+
/** Env var prefix (e.g. 'MYAPP' -> `MYAPP_…`). Default: '' (no prefix). */
|
|
18
|
+
prefix?: string;
|
|
19
|
+
/** The environment to read. Default: the ambient `process.env` (or `{}` in a non-Node runtime). */
|
|
20
|
+
env?: Record<string, string | undefined>;
|
|
21
|
+
/** Coerce a raw string env value to a setting value. Default: `defaultCoerce`. */
|
|
22
|
+
coerce?: (raw: string, key: string) => unknown;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Map a dotted setting key to its env var name: PREFIX + the path with `.`/`-`/`/` -> `__` and
|
|
26
|
+
* camelCase -> `_`, uppercased. e.g. `editor.fontSize` (prefix `MYAPP`) -> `MYAPP_EDITOR__FONT_SIZE`.
|
|
27
|
+
*/
|
|
28
|
+
declare function envVarName(key: string, prefix?: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Coerce a raw env string: booleans, numbers, and JSON objects/arrays are parsed; else the string.
|
|
31
|
+
* A numeric-looking string is only coerced when it ROUND-TRIPS exactly (`String(Number(x)) === x`),
|
|
32
|
+
* so leading-zero values ("007"), big integers beyond MAX_SAFE_INTEGER, signed zero, etc. are kept
|
|
33
|
+
* as strings rather than silently corrupted.
|
|
34
|
+
*/
|
|
35
|
+
declare function defaultCoerce(raw: string): unknown;
|
|
36
|
+
/** Create a read-only environment-variable LayerStore. */
|
|
37
|
+
declare function createEnvStore(options?: EnvStoreOptions): LayerStore;
|
|
38
|
+
|
|
39
|
+
export { type EnvStoreOptions, createEnvStore, defaultCoerce, envVarName };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
function envVarName(key, prefix = "") {
|
|
3
|
+
const body = key.replace(/[.\-/]+/g, "__").replace(/([a-z0-9])([A-Z])/g, "$1_$2").toUpperCase();
|
|
4
|
+
return prefix ? `${prefix.replace(/_+$/, "")}_${body}` : body;
|
|
5
|
+
}
|
|
6
|
+
function defaultCoerce(raw) {
|
|
7
|
+
const trimmed = raw.trim();
|
|
8
|
+
if (trimmed === "true") return true;
|
|
9
|
+
if (trimmed === "false") return false;
|
|
10
|
+
if (/^-?\d+(?:\.\d+)?$/.test(trimmed)) {
|
|
11
|
+
const n = Number(trimmed);
|
|
12
|
+
return String(n) === trimmed ? n : raw;
|
|
13
|
+
}
|
|
14
|
+
if (trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
15
|
+
try {
|
|
16
|
+
return JSON.parse(trimmed);
|
|
17
|
+
} catch {
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return raw;
|
|
21
|
+
}
|
|
22
|
+
function ambientEnv() {
|
|
23
|
+
const proc = globalThis.process;
|
|
24
|
+
return proc?.env ?? {};
|
|
25
|
+
}
|
|
26
|
+
function createEnvStore(options = {}) {
|
|
27
|
+
const scope = options.scope ?? "env";
|
|
28
|
+
const prefix = options.prefix ?? "";
|
|
29
|
+
const keys = options.keys ?? [];
|
|
30
|
+
const coerce = options.coerce ?? defaultCoerce;
|
|
31
|
+
const claimed = /* @__PURE__ */ new Map();
|
|
32
|
+
for (const key of keys) {
|
|
33
|
+
const name = envVarName(key, prefix);
|
|
34
|
+
const prior = claimed.get(name);
|
|
35
|
+
if (prior !== void 0 && prior !== key) {
|
|
36
|
+
throw new Error(`@zodal/dials-store-env: keys "${prior}" and "${key}" both map to env var ${name}`);
|
|
37
|
+
}
|
|
38
|
+
claimed.set(name, key);
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
scope,
|
|
42
|
+
getCapabilities: () => ({ readable: true, writable: false, watchable: false }),
|
|
43
|
+
load() {
|
|
44
|
+
const env = options.env ?? ambientEnv();
|
|
45
|
+
const layer = {};
|
|
46
|
+
for (const key of keys) {
|
|
47
|
+
const raw = env[envVarName(key, prefix)];
|
|
48
|
+
if (raw !== void 0) layer[key] = coerce(raw, key);
|
|
49
|
+
}
|
|
50
|
+
return Promise.resolve(layer);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export {
|
|
55
|
+
createEnvStore,
|
|
56
|
+
defaultCoerce,
|
|
57
|
+
envVarName
|
|
58
|
+
};
|
|
59
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @zodal/dials-store-env — an environment-variable `LayerStore` for zodal-dials.\n *\n * Env is a high-precedence, READ-MOSTLY scope: it produces a layer from `process.env` (or any\n * injected env map) using one deterministic dotted-key -> ENV_VAR mapping, coercing string values to\n * settings values. It is key-driven (you supply the setting keys to look for) and never writes.\n */\n\nimport type { Layer, LayerStore, LayerStoreCapabilities } from '@zodal/dials-core';\n\nexport interface EnvStoreOptions {\n /** Scope id. Default: 'env'. */\n scope?: string;\n /** The setting keys to read (their env var names are derived). Env is key-driven — supply the keys\n * you expect to be overridable via the environment. */\n keys?: string[];\n /** Env var prefix (e.g. 'MYAPP' -> `MYAPP_…`). Default: '' (no prefix). */\n prefix?: string;\n /** The environment to read. Default: the ambient `process.env` (or `{}` in a non-Node runtime). */\n env?: Record<string, string | undefined>;\n /** Coerce a raw string env value to a setting value. Default: `defaultCoerce`. */\n coerce?: (raw: string, key: string) => unknown;\n}\n\n/**\n * Map a dotted setting key to its env var name: PREFIX + the path with `.`/`-`/`/` -> `__` and\n * camelCase -> `_`, uppercased. e.g. `editor.fontSize` (prefix `MYAPP`) -> `MYAPP_EDITOR__FONT_SIZE`.\n */\nexport function envVarName(key: string, prefix = ''): string {\n const body = key\n .replace(/[.\\-/]+/g, '__')\n .replace(/([a-z0-9])([A-Z])/g, '$1_$2')\n .toUpperCase();\n return prefix ? `${prefix.replace(/_+$/, '')}_${body}` : body;\n}\n\n/**\n * Coerce a raw env string: booleans, numbers, and JSON objects/arrays are parsed; else the string.\n * A numeric-looking string is only coerced when it ROUND-TRIPS exactly (`String(Number(x)) === x`),\n * so leading-zero values (\"007\"), big integers beyond MAX_SAFE_INTEGER, signed zero, etc. are kept\n * as strings rather than silently corrupted.\n */\nexport function defaultCoerce(raw: string): unknown {\n const trimmed = raw.trim();\n if (trimmed === 'true') return true;\n if (trimmed === 'false') return false;\n if (/^-?\\d+(?:\\.\\d+)?$/.test(trimmed)) {\n const n = Number(trimmed);\n return String(n) === trimmed ? n : raw;\n }\n if ((trimmed.startsWith('{') && trimmed.endsWith('}')) || (trimmed.startsWith('[') && trimmed.endsWith(']'))) {\n try {\n return JSON.parse(trimmed);\n } catch {\n // not valid JSON — fall through to the raw string\n }\n }\n return raw;\n}\n\nfunction ambientEnv(): Record<string, string | undefined> {\n const proc = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process;\n return proc?.env ?? {};\n}\n\n/** Create a read-only environment-variable LayerStore. */\nexport function createEnvStore(options: EnvStoreOptions = {}): LayerStore {\n const scope = options.scope ?? 'env';\n const prefix = options.prefix ?? '';\n const keys = options.keys ?? [];\n const coerce = options.coerce ?? defaultCoerce;\n\n // The key -> env-var mapping is not injective (e.g. `a.b`, `a-b`, `a/b` all map to `A__B`). Detect\n // collisions at construction so two distinct keys can never silently read the same variable.\n const claimed = new Map<string, string>();\n for (const key of keys) {\n const name = envVarName(key, prefix);\n const prior = claimed.get(name);\n if (prior !== undefined && prior !== key) {\n throw new Error(`@zodal/dials-store-env: keys \"${prior}\" and \"${key}\" both map to env var ${name}`);\n }\n claimed.set(name, key);\n }\n\n return {\n scope,\n getCapabilities: (): LayerStoreCapabilities => ({ readable: true, writable: false, watchable: false }),\n load(): Promise<Layer> {\n const env = options.env ?? ambientEnv();\n const layer: Layer = {};\n for (const key of keys) {\n const raw = env[envVarName(key, prefix)];\n if (raw !== undefined) layer[key] = coerce(raw, key);\n }\n return Promise.resolve(layer);\n },\n };\n}\n"],"mappings":";AA4BO,SAAS,WAAW,KAAa,SAAS,IAAY;AAC3D,QAAM,OAAO,IACV,QAAQ,YAAY,IAAI,EACxB,QAAQ,sBAAsB,OAAO,EACrC,YAAY;AACf,SAAO,SAAS,GAAG,OAAO,QAAQ,OAAO,EAAE,CAAC,IAAI,IAAI,KAAK;AAC3D;AAQO,SAAS,cAAc,KAAsB;AAClD,QAAM,UAAU,IAAI,KAAK;AACzB,MAAI,YAAY,OAAQ,QAAO;AAC/B,MAAI,YAAY,QAAS,QAAO;AAChC,MAAI,oBAAoB,KAAK,OAAO,GAAG;AACrC,UAAM,IAAI,OAAO,OAAO;AACxB,WAAO,OAAO,CAAC,MAAM,UAAU,IAAI;AAAA,EACrC;AACA,MAAK,QAAQ,WAAW,GAAG,KAAK,QAAQ,SAAS,GAAG,KAAO,QAAQ,WAAW,GAAG,KAAK,QAAQ,SAAS,GAAG,GAAI;AAC5G,QAAI;AACF,aAAO,KAAK,MAAM,OAAO;AAAA,IAC3B,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,aAAiD;AACxD,QAAM,OAAQ,WAA0E;AACxF,SAAO,MAAM,OAAO,CAAC;AACvB;AAGO,SAAS,eAAe,UAA2B,CAAC,GAAe;AACxE,QAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,OAAO,QAAQ,QAAQ,CAAC;AAC9B,QAAM,SAAS,QAAQ,UAAU;AAIjC,QAAM,UAAU,oBAAI,IAAoB;AACxC,aAAW,OAAO,MAAM;AACtB,UAAM,OAAO,WAAW,KAAK,MAAM;AACnC,UAAM,QAAQ,QAAQ,IAAI,IAAI;AAC9B,QAAI,UAAU,UAAa,UAAU,KAAK;AACxC,YAAM,IAAI,MAAM,iCAAiC,KAAK,UAAU,GAAG,yBAAyB,IAAI,EAAE;AAAA,IACpG;AACA,YAAQ,IAAI,MAAM,GAAG;AAAA,EACvB;AAEA,SAAO;AAAA,IACL;AAAA,IACA,iBAAiB,OAA+B,EAAE,UAAU,MAAM,UAAU,OAAO,WAAW,MAAM;AAAA,IACpG,OAAuB;AACrB,YAAM,MAAM,QAAQ,OAAO,WAAW;AACtC,YAAM,QAAe,CAAC;AACtB,iBAAW,OAAO,MAAM;AACtB,cAAM,MAAM,IAAI,WAAW,KAAK,MAAM,CAAC;AACvC,YAAI,QAAQ,OAAW,OAAM,GAAG,IAAI,OAAO,KAAK,GAAG;AAAA,MACrD;AACA,aAAO,QAAQ,QAAQ,KAAK;AAAA,IAC9B;AAAA,EACF;AACF;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zodal/dials-store-env",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Environment-variable LayerStore for zodal-dials (a high-precedence, read-mostly scope)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"require": {
|
|
16
|
+
"types": "./dist/index.d.cts",
|
|
17
|
+
"default": "./dist/index.cjs"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"@zodal/core": "^0.1.2",
|
|
29
|
+
"@zodal/dials-core": "^0.1.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@zodal/core": "^0.1.2",
|
|
33
|
+
"tsup": "^8.0.0",
|
|
34
|
+
"typescript": "^5.7.0",
|
|
35
|
+
"vitest": "^3.0.0",
|
|
36
|
+
"@zodal/dials-core": "0.1.0"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsup",
|
|
40
|
+
"test": "vitest run",
|
|
41
|
+
"typecheck": "tsc --noEmit"
|
|
42
|
+
}
|
|
43
|
+
}
|