@nifrajs/env 5.0.0 → 5.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 CHANGED
@@ -64,8 +64,11 @@ pass the request's bindings: `defineEnv(shape, { source: env })`.
64
64
  ## Secret safety
65
65
 
66
66
  Error messages name the offending variable and the reason - **never its value** (it may be a
67
- secret). A BYO validator such as valibot may quote the value in its message; `defineEnv` replaces
68
- it with `<redacted>`. `defineEnv` returns a frozen object, so a validated config can't be mutated downstream.
67
+ secret). A BYO validator such as valibot or zod may quote the value in its message, escaped or
68
+ cut short, so when it refuses a set value `defineEnv` reports the variable as invalid without the
69
+ validator's message. For an unset variable the message is shown, since there is no value to quote.
70
+ Use the `env.*` helpers when you want the reason as well. `defineEnv` returns a frozen object, so a
71
+ validated config can't be mutated downstream.
69
72
 
70
73
  ## For AI agents
71
74
 
package/dist/define.d.ts CHANGED
@@ -31,10 +31,11 @@ export interface DefineEnvOptions {
31
31
  * // ENV.PORT: number, ENV.NODE_ENV: "development" | "production" | "test", … all validated.
32
32
  * ```
33
33
  *
34
- * Errors name the offending variable and the reason - never its value (it may be a secret); a BYO
35
- * validator's message that quotes the value gets `<redacted>` in its place. BYO
36
- * validators (`t`, zod, valibot) work too; only the `env.*` helpers coerce from strings, so a plain
37
- * `t.number()` would see the raw string - use `env.number()` for coercion.
34
+ * Errors name the offending variable and the reason - never its value (it may be a secret). BYO
35
+ * validators (`t`, zod, valibot) work too, but their messages may quote the value, so a set value
36
+ * they refuse is reported as invalid without their words; an unset one shows their message. Only the
37
+ * `env.*` helpers coerce from strings, so a plain `t.number()` would see the raw string - use
38
+ * `env.number()` for coercion.
38
39
  */
39
40
  export declare function defineEnv<S extends EnvShape>(shape: S, options?: DefineEnvOptions): EnvResult<S>;
40
41
  //# sourceMappingURL=define.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"define.d.ts","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAEhE,kCAAkC;AAClC,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;AAEvD,4EAA4E;AAC5E,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,QAAQ,IAAI;IAAE,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,CAAA;AAE1F,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;CACrD;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC,CAAC,CAAC,CAsChG"}
1
+ {"version":3,"file":"define.d.ts","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAEhE,kCAAkC;AAClC,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;AAEvD,4EAA4E;AAC5E,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,QAAQ,IAAI;IAAE,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,CAAA;AAE1F,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;CACrD;AAID;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC,CAAC,CAAC,CAuChG"}
package/dist/define.js CHANGED
@@ -1,3 +1,4 @@
1
+ const WITHHELD = "is invalid (the validator's message is withheld, since it may quote the value)";
1
2
  /**
2
3
  * Validate the environment against a schema and return a **frozen, typed** object - or throw at
3
4
  * startup listing **every** problem at once (not just the first), so a misconfigured deploy fails
@@ -17,10 +18,11 @@
17
18
  * // ENV.PORT: number, ENV.NODE_ENV: "development" | "production" | "test", … all validated.
18
19
  * ```
19
20
  *
20
- * Errors name the offending variable and the reason - never its value (it may be a secret); a BYO
21
- * validator's message that quotes the value gets `<redacted>` in its place. BYO
22
- * validators (`t`, zod, valibot) work too; only the `env.*` helpers coerce from strings, so a plain
23
- * `t.number()` would see the raw string - use `env.number()` for coercion.
21
+ * Errors name the offending variable and the reason - never its value (it may be a secret). BYO
22
+ * validators (`t`, zod, valibot) work too, but their messages may quote the value, so a set value
23
+ * they refuse is reported as invalid without their words; an unset one shows their message. Only the
24
+ * `env.*` helpers coerce from strings, so a plain `t.number()` would see the raw string - use
25
+ * `env.number()` for coercion.
24
26
  */
25
27
  export function defineEnv(shape, options) {
26
28
  const source = options?.source ??
@@ -41,12 +43,14 @@ export function defineEnv(shape, options) {
41
43
  }
42
44
  if (result.issues !== undefined) {
43
45
  const raw = source[key];
44
- // Only the `env.*` helpers promise value-free messages; valibot and hand-rolled validators
45
- // quote the value, which may be a secret.
46
- const foreign = schema["~standard"].vendor !== "nifra-env" && raw !== undefined && raw !== "";
47
- for (const i of result.issues) {
48
- problems.push(` ${key}: ${foreign ? i.message.replaceAll(raw, "<redacted>") : i.message}`);
46
+ // Only the `env.*` helpers promise value-free messages. Another validator may quote the value
47
+ // escaped, truncated or transformed, past any search-and-replace, so its words are not shown.
48
+ if (schema["~standard"].vendor !== "nifra-env" && raw !== undefined && raw !== "") {
49
+ problems.push(` ${key}: ${WITHHELD}`);
50
+ continue;
49
51
  }
52
+ for (const i of result.issues)
53
+ problems.push(` ${key}: ${i.message}`);
50
54
  }
51
55
  else {
52
56
  out[key] = result.value;
@@ -1 +1 @@
1
- {"version":3,"file":"define.js","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,SAAS,CAAqB,KAAQ,EAAE,OAA0B;IAChF,MAAM,MAAM,GACV,OAAO,EAAE,MAAM;QACd,UAAyE,CAAC,OAAO,EAAE,GAAG;QACvF,EAAE,CAAA;IACJ,MAAM,GAAG,GAA4B,EAAE,CAAA;IACvC,MAAM,QAAQ,GAAa,EAAE,CAAA;IAE7B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAqB,CAAA;QAC7C,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA;QACxD,IAAI,MAAM,YAAY,OAAO,EAAE,CAAC;YAC9B,yFAAyF;YACzF,8FAA8F;YAC9F,8DAA8D;YAC9D,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;YACtB,QAAQ,CAAC,IAAI,CAAC,KAAK,GAAG,mDAAmD,CAAC,CAAA;YAC1E,SAAQ;QACV,CAAC;QACD,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAChC,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;YACvB,2FAA2F;YAC3F,0CAA0C;YAC1C,MAAM,OAAO,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,MAAM,KAAK,WAAW,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,EAAE,CAAA;YAC7F,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;gBAC9B,QAAQ,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAA;YAC7F,CAAC;QACH,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,KAAK,CAAA;QACzB,CAAC;IACH,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,qCAAqC,QAAQ,CAAC,MAAM,iBAAiB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,4CAA4C,CACrI,CAAA;IACH,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,CAAiB,CAAA;AAC3C,CAAC"}
1
+ {"version":3,"file":"define.js","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"AAgBA,MAAM,QAAQ,GAAG,gFAAgF,CAAA;AAEjG;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,SAAS,CAAqB,KAAQ,EAAE,OAA0B;IAChF,MAAM,MAAM,GACV,OAAO,EAAE,MAAM;QACd,UAAyE,CAAC,OAAO,EAAE,GAAG;QACvF,EAAE,CAAA;IACJ,MAAM,GAAG,GAA4B,EAAE,CAAA;IACvC,MAAM,QAAQ,GAAa,EAAE,CAAA;IAE7B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAqB,CAAA;QAC7C,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA;QACxD,IAAI,MAAM,YAAY,OAAO,EAAE,CAAC;YAC9B,yFAAyF;YACzF,8FAA8F;YAC9F,8DAA8D;YAC9D,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;YACtB,QAAQ,CAAC,IAAI,CAAC,KAAK,GAAG,mDAAmD,CAAC,CAAA;YAC1E,SAAQ;QACV,CAAC;QACD,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAChC,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;YACvB,8FAA8F;YAC9F,8FAA8F;YAC9F,IAAI,MAAM,CAAC,WAAW,CAAC,CAAC,MAAM,KAAK,WAAW,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,EAAE,EAAE,CAAC;gBAClF,QAAQ,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,QAAQ,EAAE,CAAC,CAAA;gBACtC,SAAQ;YACV,CAAC;YACD,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM;gBAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAA;QACxE,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,KAAK,CAAA;QACzB,CAAC;IACH,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,qCAAqC,QAAQ,CAAC,MAAM,iBAAiB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,4CAA4C,CACrI,CAAA;IACH,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,CAAiB,CAAA;AAC3C,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nifrajs/env",
3
- "version": "5.0.0",
3
+ "version": "5.1.0",
4
4
  "description": "Typed, validated environment variables for nifra - define a schema, get a frozen typed object, fail loud at boot listing every problem. Coercing helpers (number/port/boolean/url/enum) + any Standard Schema. Dependency-free, edge-safe.",
5
5
  "keywords": [
6
6
  "bun",
package/src/define.ts CHANGED
@@ -14,6 +14,8 @@ export interface DefineEnvOptions {
14
14
  readonly source?: Record<string, string | undefined>
15
15
  }
16
16
 
17
+ const WITHHELD = "is invalid (the validator's message is withheld, since it may quote the value)"
18
+
17
19
  /**
18
20
  * Validate the environment against a schema and return a **frozen, typed** object - or throw at
19
21
  * startup listing **every** problem at once (not just the first), so a misconfigured deploy fails
@@ -33,10 +35,11 @@ export interface DefineEnvOptions {
33
35
  * // ENV.PORT: number, ENV.NODE_ENV: "development" | "production" | "test", … all validated.
34
36
  * ```
35
37
  *
36
- * Errors name the offending variable and the reason - never its value (it may be a secret); a BYO
37
- * validator's message that quotes the value gets `<redacted>` in its place. BYO
38
- * validators (`t`, zod, valibot) work too; only the `env.*` helpers coerce from strings, so a plain
39
- * `t.number()` would see the raw string - use `env.number()` for coercion.
38
+ * Errors name the offending variable and the reason - never its value (it may be a secret). BYO
39
+ * validators (`t`, zod, valibot) work too, but their messages may quote the value, so a set value
40
+ * they refuse is reported as invalid without their words; an unset one shows their message. Only the
41
+ * `env.*` helpers coerce from strings, so a plain `t.number()` would see the raw string - use
42
+ * `env.number()` for coercion.
40
43
  */
41
44
  export function defineEnv<S extends EnvShape>(shape: S, options?: DefineEnvOptions): EnvResult<S> {
42
45
  const source =
@@ -59,12 +62,13 @@ export function defineEnv<S extends EnvShape>(shape: S, options?: DefineEnvOptio
59
62
  }
60
63
  if (result.issues !== undefined) {
61
64
  const raw = source[key]
62
- // Only the `env.*` helpers promise value-free messages; valibot and hand-rolled validators
63
- // quote the value, which may be a secret.
64
- const foreign = schema["~standard"].vendor !== "nifra-env" && raw !== undefined && raw !== ""
65
- for (const i of result.issues) {
66
- problems.push(` ${key}: ${foreign ? i.message.replaceAll(raw, "<redacted>") : i.message}`)
65
+ // Only the `env.*` helpers promise value-free messages. Another validator may quote the value
66
+ // escaped, truncated or transformed, past any search-and-replace, so its words are not shown.
67
+ if (schema["~standard"].vendor !== "nifra-env" && raw !== undefined && raw !== "") {
68
+ problems.push(` ${key}: ${WITHHELD}`)
69
+ continue
67
70
  }
71
+ for (const i of result.issues) problems.push(` ${key}: ${i.message}`)
68
72
  } else {
69
73
  out[key] = result.value
70
74
  }