@cleverbrush/env 4.3.2 → 4.4.1

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
@@ -45,8 +45,8 @@ npm install @cleverbrush/env @cleverbrush/schema
45
45
  ### Structured config (nested)
46
46
 
47
47
  ```typescript
48
- import { env, parseEnv, splitBy } from '@cleverbrush/env';
49
- import { string, number, boolean, array } from '@cleverbrush/schema';
48
+ import { env, envBoolean, parseEnv, splitBy } from '@cleverbrush/env';
49
+ import { string, number, array } from '@cleverbrush/schema';
50
50
 
51
51
  const config = parseEnv({
52
52
  db: {
@@ -57,7 +57,7 @@ const config = parseEnv({
57
57
  jwt: {
58
58
  secret: env('JWT_SECRET', string().minLength(32)),
59
59
  },
60
- debug: env('DEBUG', boolean().coerce().default(false)),
60
+ debug: env('DEBUG', envBoolean().default(false)),
61
61
  allowedOrigins: env(
62
62
  'ALLOWED_ORIGINS',
63
63
  array(string()).addPreprocessor(splitBy(','), { mutates: false })
@@ -115,6 +115,22 @@ env('PORTS', array(number().coerce()).addPreprocessor(splitBy(','), { mutates: f
115
115
  // "3000, 4000" → [3000, 4000]
116
116
  ```
117
117
 
118
+ ### Environment booleans
119
+
120
+ Use `envBoolean()` for shell-style boolean flags. It accepts `true` / `false`,
121
+ `1` / `0`, `yes` / `no`, and `on` / `off` by default:
122
+
123
+ ```typescript
124
+ import { env, envBoolean } from '@cleverbrush/env';
125
+
126
+ env('FEATURE_ENABLED', envBoolean().default(false));
127
+ // FEATURE_ENABLED=1 → true
128
+ // FEATURE_ENABLED=off → false
129
+ ```
130
+
131
+ Pass `trueValues`, `falseValues`, and `caseSensitive` when a project has its
132
+ own flag vocabulary.
133
+
118
134
  ### Error reporting
119
135
 
120
136
  When variables are missing or invalid, `EnvValidationError` is thrown with a formatted message:
@@ -188,6 +204,7 @@ const config = parseEnv(
188
204
  | `parseEnv(config, source?)` | Function | Parses env vars into a validated, typed nested config object. |
189
205
  | `parseEnv(config, compute, source?)` | Function | Parses env vars, then deep-merges computed values from the callback. |
190
206
  | `parseEnvFlat(schemas, source?)` | Function | Flat convenience — keys are env var names, no `env()` needed. |
207
+ | `envBoolean(options?)` | Function | Boolean schema for env-style values such as `1`, `0`, `yes`, `no`, `on`, `off`. |
191
208
  | `splitBy(separator)` | Function | Preprocessor that splits a string into an array. |
192
209
  | `EnvValidationError` | Class | Thrown when env vars are missing or invalid. Has `.missing` and `.invalid`. |
193
210
  | `EnvField<T>` | Type | Branded wrapper type created by `env()`. |
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Options for {@link envBoolean}.
3
+ */
4
+ export interface EnvBooleanOptions {
5
+ /**
6
+ * String values accepted as `true`.
7
+ *
8
+ * @defaultValue `['true', '1', 'yes', 'on']`
9
+ */
10
+ trueValues?: readonly string[];
11
+ /**
12
+ * String values accepted as `false`.
13
+ *
14
+ * @defaultValue `['false', '0', 'no', 'off']`
15
+ */
16
+ falseValues?: readonly string[];
17
+ /**
18
+ * Whether string matching is case-sensitive.
19
+ *
20
+ * @defaultValue `false`
21
+ */
22
+ caseSensitive?: boolean;
23
+ /**
24
+ * Whether to trim surrounding whitespace before matching.
25
+ *
26
+ * @defaultValue `true`
27
+ */
28
+ trim?: boolean;
29
+ }
30
+ /**
31
+ * Create a boolean schema tuned for environment variables.
32
+ *
33
+ * The base schema `boolean().coerce()` intentionally accepts only
34
+ * `"true"`/`"false"`. Environment variables often use shell-style toggles
35
+ * such as `1`, `0`, `yes`, `no`, `on`, and `off`, so this helper normalizes
36
+ * those values before boolean validation runs.
37
+ *
38
+ * @param options - Matching behavior and accepted true/false strings.
39
+ * @returns A boolean schema builder with env-style string preprocessing.
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * const config = parseEnv({
44
+ * debug: env('DEBUG', envBoolean().default(false)),
45
+ * });
46
+ * ```
47
+ */
48
+ export declare function envBoolean(options?: EnvBooleanOptions): import("@cleverbrush/schema").ExtendedBoolean;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export { env } from './env.js';
2
+ export type { EnvBooleanOptions } from './envBoolean.js';
3
+ export { envBoolean } from './envBoolean.js';
2
4
  export type { InvalidEnvVar, MissingEnvVar } from './errors.js';
3
5
  export { EnvValidationError } from './errors.js';
4
6
  export { parseEnv } from './parseEnv.js';
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- var p=Symbol("ENV_FIELD_BRAND");function l(n,e){if(typeof n!="string"||!n)throw new Error("env(): varName must be a non-empty string");return{[p]:!0,varName:n,schema:e}}var d=class extends Error{missing;invalid;constructor(e,t){let r=[];if(e.length>0){r.push("Missing environment variables:");for(let o of e)r.push(` - ${o.varName} (required by ${o.configPath}) [${o.type}]`)}if(t.length>0){r.push("Invalid environment variables:");for(let o of t)r.push(` - ${o.varName}: ${JSON.stringify(o.value)} (required by ${o.configPath}) \u2014 ${o.errors.join("; ")}`)}super(r.join(`
2
- `)),this.name="EnvValidationError",this.missing=e,this.invalid=t}};import{deepExtend as w}from"@cleverbrush/deep";import{object as k}from"@cleverbrush/schema";function T(n){return typeof n=="object"&&n!==null&&p in n&&n[p]===!0}function x(n,e,t){let r={},o=[];for(let f of Object.keys(n)){let s=n[f],v=t?`${t}.${f}`:f;if(T(s)){let a=e[s.varName];r[f]=a===""?void 0:a,o.push({varName:s.varName,configPath:v,schema:s.schema})}else if(typeof s=="object"&&s!==null){let a=x(s,e,v);r[f]=a.rawObject,o.push(...a.mappings)}}return{rawObject:r,mappings:o}}function N(n){let e={};for(let t of Object.keys(n)){let r=n[t];T(r)?e[t]=r.schema:typeof r=="object"&&r!==null&&(e[t]=N(r))}return k(e)}function m(n,e,t){let r=typeof e=="function",o=r?t??(typeof process<"u"?process.env:{}):e??(typeof process<"u"?process.env:{}),{rawObject:f,mappings:s}=x(n,o,""),a=N(n).validate(f,{doNotStopOnFirstError:!0});if(a.valid){let i=a.object;if(r){let c=e(i);return w(i,c)}return i}let y=[],g=[],E=a.errors?.map(i=>i.message)??[];for(let i of s){let c=o[i.varName],u=i.schema.introspect(),R=u.isRequired,b=u.hasDefault;if(c===void 0||c==="")R&&!b&&y.push({varName:i.varName,configPath:i.configPath,type:u.type});else{let h=i.schema.validate(c);h.valid||g.push({varName:i.varName,configPath:i.configPath,value:c,errors:h.errors?.map(I=>I.message)??[]})}}throw y.length===0&&g.length===0&&E.length>0&&g.push({varName:"(unknown)",configPath:"(unknown)",value:"",errors:E}),new d(y,g)}function C(n,e){let t={};for(let r of Object.keys(n))t[r]=l(r,n[r]);return m(t,e)}function F(n){return(e=>typeof e=="string"?e.split(n).map(t=>t.trim()):e)}export{d as EnvValidationError,l as env,m as parseEnv,C as parseEnvFlat,F as splitBy};
1
+ var g=Symbol("ENV_FIELD_BRAND");function y(n,e){if(typeof n!="string"||!n)throw new Error("env(): varName must be a non-empty string");return{[g]:!0,varName:n,schema:e}}import{boolean as I}from"@cleverbrush/schema";var F=["true","1","yes","on"],C=["false","0","no","off"];function S(n={}){let{trueValues:e=F,falseValues:t=C,caseSensitive:r=!1,trim:o=!0}=n,a=s=>{let c=o?s.trim():s;return r?c:c.toLowerCase()},f=new Set(e.map(a)),d=new Set(t.map(a));return I().addPreprocessor(s=>{if(typeof s!="string")return s;let c=a(s);return f.has(c)?!0:d.has(c)?!1:s},{mutates:!0})}var u=class extends Error{missing;invalid;constructor(e,t){let r=[];if(e.length>0){r.push("Missing environment variables:");for(let o of e)r.push(` - ${o.varName} (required by ${o.configPath}) [${o.type}]`)}if(t.length>0){r.push("Invalid environment variables:");for(let o of t)r.push(` - ${o.varName}: ${JSON.stringify(o.value)} (required by ${o.configPath}) \u2014 ${o.errors.join("; ")}`)}super(r.join(`
2
+ `)),this.name="EnvValidationError",this.missing=e,this.invalid=t}};import{deepExtend as k}from"@cleverbrush/deep";import{object as V}from"@cleverbrush/schema";function T(n){return typeof n=="object"&&n!==null&&g in n&&n[g]===!0}function x(n,e,t){let r={},o=[];for(let a of Object.keys(n)){let f=n[a],d=t?`${t}.${a}`:a;if(T(f)){let s=e[f.varName];r[a]=s===""?void 0:s,o.push({varName:f.varName,configPath:d,schema:f.schema})}else if(typeof f=="object"&&f!==null){let s=x(f,e,d);r[a]=s.rawObject,o.push(...s.mappings)}}return{rawObject:r,mappings:o}}function b(n){let e={};for(let t of Object.keys(n)){let r=n[t];T(r)?e[t]=r.schema:typeof r=="object"&&r!==null&&(e[t]=b(r))}return V(e)}function m(n,e,t){let r=typeof e=="function",o=r?t??(typeof process<"u"?process.env:{}):e??(typeof process<"u"?process.env:{}),{rawObject:a,mappings:f}=x(n,o,""),s=b(n).validate(a,{doNotStopOnFirstError:!0});if(s.valid){let i=s.object;if(r){let p=e(i);return k(i,p)}return i}let c=[],v=[],E=s.errors?.map(i=>i.message)??[];for(let i of f){let p=o[i.varName],l=i.schema.introspect(),R=l.isRequired,N=l.hasDefault;if(p===void 0||p==="")R&&!N&&c.push({varName:i.varName,configPath:i.configPath,type:l.type});else{let h=i.schema.validate(p);h.valid||v.push({varName:i.varName,configPath:i.configPath,value:p,errors:h.errors?.map(w=>w.message)??[]})}}throw c.length===0&&v.length===0&&E.length>0&&v.push({varName:"(unknown)",configPath:"(unknown)",value:"",errors:E}),new u(c,v)}function B(n,e){let t={};for(let r of Object.keys(n))t[r]=y(r,n[r]);return m(t,e)}function j(n){return(e=>typeof e=="string"?e.split(n).map(t=>t.trim()):e)}export{u as EnvValidationError,y as env,S as envBoolean,m as parseEnv,B as parseEnvFlat,j as splitBy};
3
3
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts","../src/env.ts","../src/errors.ts","../src/parseEnv.ts","../src/parseEnvFlat.ts","../src/splitBy.ts"],"sourcesContent":["import type { InferType, SchemaBuilder } from '@cleverbrush/schema';\n\n/**\n * Brand symbol used to distinguish EnvField from plain objects at the type level.\n */\nexport const ENV_FIELD_BRAND: unique symbol = Symbol('ENV_FIELD_BRAND');\n\n/**\n * A branded wrapper around a schema builder that associates it with an\n * environment variable name. Created exclusively by the {@link env} function.\n */\nexport type EnvField<T extends SchemaBuilder<any, any, any, any, any>> = {\n readonly [ENV_FIELD_BRAND]: true;\n readonly varName: string;\n readonly schema: T;\n};\n\n/**\n * A node in the config descriptor tree.\n * - Leaf: an `EnvField` (created via `env()`)\n * - Branch: a plain object whose values are themselves `EnvConfigNode`s\n *\n * Schema builders without `env()` wrapping intentionally fail to satisfy\n * this type, producing a compile-time error.\n */\nexport type EnvConfigNode =\n | EnvField<any>\n | { readonly [key: string]: EnvConfigNode };\n\n/**\n * Top-level config descriptor: a record of `EnvConfigNode`s.\n */\nexport type EnvConfig = Record<string, EnvConfigNode>;\n\n/**\n * Recursively infers the runtime type from an `EnvConfig` descriptor tree.\n *\n * - `EnvField<S>` leaves resolve to `InferType<S>`\n * - Object branches resolve to `{ [K]: InferEnvConfig<V> }`\n */\nexport type InferEnvConfig<T> = {\n [K in keyof T]: T[K] extends EnvField<infer S>\n ? InferType<S>\n : T[K] extends Record<string, any>\n ? InferEnvConfig<T[K]>\n : never;\n};\n","import type { SchemaBuilder } from '@cleverbrush/schema';\nimport { ENV_FIELD_BRAND, type EnvField } from './types.js';\n\n/**\n * Associates a schema builder with an environment variable name.\n *\n * Every leaf field in a config descriptor passed to `parseEnv()` must be\n * wrapped with `env()`. This is enforced at the TypeScript level — passing\n * a bare schema builder produces a compile-time error.\n *\n * @param varName - The environment variable name to read (e.g. `'DB_HOST'`).\n * @param schema - A `@cleverbrush/schema` builder describing the expected\n * type, constraints, coercion, and defaults.\n * @returns A branded `EnvField` descriptor.\n *\n * @example\n * ```ts\n * env('DB_PORT', number().coerce().default(5432))\n * ```\n */\nexport function env<T extends SchemaBuilder<any, any, any, any, any>>(\n varName: string,\n schema: T\n): EnvField<T> {\n if (typeof varName !== 'string' || !varName) {\n throw new Error('env(): varName must be a non-empty string');\n }\n return {\n [ENV_FIELD_BRAND]: true,\n varName,\n schema\n } as EnvField<T>;\n}\n","/**\n * Describes a single missing environment variable.\n */\nexport interface MissingEnvVar {\n /** The environment variable name (e.g. `'DB_HOST'`). */\n varName: string;\n /** Dot-separated config path (e.g. `'db.host'`). */\n configPath: string;\n /** Schema type identifier (e.g. `'string'`, `'number'`). */\n type: string;\n}\n\n/**\n * Describes a single invalid environment variable.\n */\nexport interface InvalidEnvVar {\n /** The environment variable name (e.g. `'DB_PORT'`). */\n varName: string;\n /** Dot-separated config path (e.g. `'db.port'`). */\n configPath: string;\n /** The raw string value that failed validation. */\n value: string;\n /** Validation error messages. */\n errors: string[];\n}\n\n/**\n * Thrown by `parseEnv()` when one or more environment variables are missing\n * or fail validation.\n *\n * The structured `missing` and `invalid` properties allow programmatic\n * inspection, while the formatted `message` provides a human-readable\n * summary suitable for CI logs and startup output.\n */\nexport class EnvValidationError extends Error {\n public readonly missing: readonly MissingEnvVar[];\n public readonly invalid: readonly InvalidEnvVar[];\n\n constructor(missing: MissingEnvVar[], invalid: InvalidEnvVar[]) {\n const lines: string[] = [];\n\n if (missing.length > 0) {\n lines.push('Missing environment variables:');\n for (const m of missing) {\n lines.push(\n ` - ${m.varName} (required by ${m.configPath}) [${m.type}]`\n );\n }\n }\n\n if (invalid.length > 0) {\n lines.push('Invalid environment variables:');\n for (const inv of invalid) {\n lines.push(\n ` - ${inv.varName}: ${JSON.stringify(inv.value)} (required by ${inv.configPath}) — ${inv.errors.join('; ')}`\n );\n }\n }\n\n super(lines.join('\\n'));\n this.name = 'EnvValidationError';\n this.missing = missing;\n this.invalid = invalid;\n }\n}\n","import { deepExtend, type Merge } from '@cleverbrush/deep';\nimport { object, type SchemaBuilder } from '@cleverbrush/schema';\nimport {\n EnvValidationError,\n type InvalidEnvVar,\n type MissingEnvVar\n} from './errors.js';\nimport {\n ENV_FIELD_BRAND,\n type EnvConfig,\n type EnvField,\n type InferEnvConfig\n} from './types.js';\n\n/**\n * Returns `true` if the value is an `EnvField` (created by `env()`).\n */\nfunction isEnvField(value: unknown): value is EnvField<any> {\n return (\n typeof value === 'object' &&\n value !== null &&\n ENV_FIELD_BRAND in value &&\n (value as any)[ENV_FIELD_BRAND] === true\n );\n}\n\n/**\n * Metadata collected during the config tree walk, tracking which env var\n * maps to which config path for error reporting.\n */\ninterface EnvMapping {\n varName: string;\n configPath: string;\n schema: SchemaBuilder<any, any, any, any, any>;\n}\n\n/**\n * Recursively walks the config descriptor tree:\n * - For `EnvField` leaves: reads `source[varName]` and places the raw value\n * - For object branches: recurses into children\n *\n * @returns `rawObject` — a nested object with raw string values at the\n * correct paths, and `mappings` — an array tracking varName→configPath\n * for error reporting.\n */\nfunction walkConfig(\n config: Record<string, unknown>,\n source: Record<string, string | undefined>,\n pathPrefix: string\n): { rawObject: Record<string, unknown>; mappings: EnvMapping[] } {\n const rawObject: Record<string, unknown> = {};\n const mappings: EnvMapping[] = [];\n\n for (const key of Object.keys(config)) {\n const node = config[key];\n const configPath = pathPrefix ? `${pathPrefix}.${key}` : key;\n\n if (isEnvField(node)) {\n const raw = source[node.varName];\n rawObject[key] = raw === '' ? undefined : raw;\n mappings.push({\n varName: node.varName,\n configPath,\n schema: node.schema\n });\n } else if (typeof node === 'object' && node !== null) {\n const child = walkConfig(\n node as Record<string, unknown>,\n source,\n configPath\n );\n rawObject[key] = child.rawObject;\n mappings.push(...child.mappings);\n }\n }\n\n return { rawObject, mappings };\n}\n\n/**\n * Recursively builds an `ObjectSchemaBuilder` from the config descriptor tree.\n *\n * For `EnvField` leaves the inner schema is used directly.\n * For object branches a nested `object()` schema is constructed.\n */\nfunction buildSchema(\n config: Record<string, unknown>\n): SchemaBuilder<any, any, any, any, any> {\n const props: Record<string, SchemaBuilder<any, any, any, any, any>> = {};\n\n for (const key of Object.keys(config)) {\n const node = config[key];\n\n if (isEnvField(node)) {\n props[key] = node.schema;\n } else if (typeof node === 'object' && node !== null) {\n props[key] = buildSchema(node as Record<string, unknown>);\n }\n }\n\n return object(props as any) as any;\n}\n\n/**\n * Parses environment variables into a validated, typed config object.\n *\n * Every leaf field in the config descriptor must be wrapped with `env()` —\n * this is enforced at the TypeScript level.\n *\n * The function:\n * 1. Walks the descriptor tree, reading raw values from `source` (defaults\n * to `process.env`).\n * 2. Assembles a `@cleverbrush/schema` object schema from the leaf schemas.\n * 3. Validates and coerces the raw values via `schema.validate()`.\n * 4. Returns the typed result or throws an `EnvValidationError` listing\n * all missing and invalid variables.\n *\n * @param config - A descriptor tree where leaves are `EnvField`s and\n * branches are plain objects.\n * @param source - The environment variable source. Defaults to `process.env`.\n *\n * @example\n * ```ts\n * const config = parseEnv({\n * db: {\n * host: env('DB_HOST', string().default('localhost')),\n * port: env('DB_PORT', number().coerce().default(5432)),\n * },\n * debug: env('DEBUG', boolean().coerce().default(false)),\n * });\n * ```\n */\nexport function parseEnv<T extends EnvConfig>(\n config: T,\n source?: Record<string, string | undefined>\n): InferEnvConfig<T>;\nexport function parseEnv<\n T extends EnvConfig,\n C extends Record<string, unknown>\n>(\n config: T,\n compute: (base: InferEnvConfig<T>) => C,\n source?: Record<string, string | undefined>\n): Merge<[InferEnvConfig<T>, C]>;\nexport function parseEnv<T extends EnvConfig>(\n config: T,\n computeOrSource?:\n | ((base: InferEnvConfig<T>) => Record<string, unknown>)\n | Record<string, string | undefined>,\n maybeSource?: Record<string, string | undefined>\n): unknown {\n const isCompute = typeof computeOrSource === 'function';\n const source: Record<string, string | undefined> = isCompute\n ? (maybeSource ?? (typeof process !== 'undefined' ? process.env : {}))\n : (computeOrSource ??\n (typeof process !== 'undefined' ? process.env : {}));\n\n const { rawObject, mappings } = walkConfig(config, source, '');\n const schema = buildSchema(config);\n\n const result = schema.validate(rawObject, {\n doNotStopOnFirstError: true\n });\n\n if (result.valid) {\n const base = result.object as InferEnvConfig<T>;\n if (isCompute) {\n const computed = (\n computeOrSource as (\n base: InferEnvConfig<T>\n ) => Record<string, unknown>\n )(base);\n return deepExtend(base, computed);\n }\n return base;\n }\n\n // Map schema validation errors back to env var names\n const missing: MissingEnvVar[] = [];\n const invalid: InvalidEnvVar[] = [];\n\n const errorMessages = result.errors?.map(e => e.message) ?? [];\n\n // For each mapping, check if the var was present and if there's an error\n for (const mapping of mappings) {\n const raw = source[mapping.varName];\n const introspected = mapping.schema.introspect();\n const isRequired = introspected.isRequired;\n const hasDefault = introspected.hasDefault;\n\n if (raw === undefined || raw === '') {\n if (isRequired && !hasDefault) {\n missing.push({\n varName: mapping.varName,\n configPath: mapping.configPath,\n type: introspected.type\n });\n }\n } else {\n // Validate this individual field to see if it's invalid\n const fieldResult = mapping.schema.validate(raw);\n if (!fieldResult.valid) {\n invalid.push({\n varName: mapping.varName,\n configPath: mapping.configPath,\n value: raw,\n errors: fieldResult.errors?.map(e => e.message) ?? []\n });\n }\n }\n }\n\n // If we couldn't categorize any errors but validation still failed,\n // add the raw error messages as a generic invalid entry\n if (\n missing.length === 0 &&\n invalid.length === 0 &&\n errorMessages.length > 0\n ) {\n invalid.push({\n varName: '(unknown)',\n configPath: '(unknown)',\n value: '',\n errors: errorMessages\n });\n }\n\n throw new EnvValidationError(missing, invalid);\n}\n","import type { InferType, SchemaBuilder } from '@cleverbrush/schema';\nimport { env } from './env.js';\nimport { parseEnv } from './parseEnv.js';\n\n/**\n * Flat schema map: keys are environment variable names, values are schema builders.\n */\ntype FlatEnvSchemas = Record<string, SchemaBuilder<any, any, any, any, any>>;\n\n/**\n * Infers the runtime type from a flat schema map.\n */\ntype InferFlatEnv<T extends FlatEnvSchemas> = {\n [K in keyof T]: InferType<T[K]>;\n};\n\n/**\n * Convenience wrapper for simple flat configs where each key is both the\n * config property name and the environment variable name.\n *\n * Equivalent to calling `parseEnv()` with every entry wrapped in `env()`.\n *\n * @param schemas - A record mapping env var names to schema builders.\n * @param source - The environment variable source. Defaults to `process.env`.\n *\n * @example\n * ```ts\n * const config = parseEnvFlat({\n * DB_HOST: string().default('localhost'),\n * DB_PORT: number().coerce().default(5432),\n * JWT_SECRET: string().minLength(32),\n * });\n * // Type: { DB_HOST: string, DB_PORT: number, JWT_SECRET: string }\n * ```\n */\nexport function parseEnvFlat<T extends FlatEnvSchemas>(\n schemas: T,\n source?: Record<string, string | undefined>\n): InferFlatEnv<T> {\n const config: Record<string, ReturnType<typeof env>> = {};\n for (const key of Object.keys(schemas)) {\n config[key] = env(key, schemas[key]);\n }\n return parseEnv(config as any, source) as InferFlatEnv<T>;\n}\n","/**\n * Creates a preprocessor function that splits a string value by the given\n * separator and trims each resulting element.\n *\n * Intended for use with `array()` schemas to parse comma-separated (or\n * similarly delimited) environment variable values.\n *\n * @param separator - The delimiter string (e.g. `','`, `';'`, `' '`).\n * @returns A preprocessor function suitable for `schema.addPreprocessor()`.\n *\n * @example\n * ```ts\n * env('ALLOWED_ORIGINS', array(string()).addPreprocessor(splitBy(','), { mutates: false }))\n * // \"a, b, c\" → ['a', 'b', 'c']\n * ```\n */\nexport function splitBy<T = string>(separator: string): (value: T[]) => T[] {\n return ((value: unknown): unknown => {\n if (typeof value === 'string') {\n return value.split(separator).map(s => s.trim());\n }\n return value;\n }) as (value: T[]) => T[];\n}\n"],"mappings":"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","names":["ENV_FIELD_BRAND","env","varName","schema","ENV_FIELD_BRAND","EnvValidationError","missing","invalid","lines","m","inv","deepExtend","object","isEnvField","value","ENV_FIELD_BRAND","walkConfig","config","source","pathPrefix","rawObject","mappings","key","node","configPath","raw","child","buildSchema","props","object","parseEnv","computeOrSource","maybeSource","isCompute","result","base","computed","deepExtend","missing","invalid","errorMessages","e","mapping","introspected","isRequired","hasDefault","fieldResult","EnvValidationError","parseEnvFlat","schemas","source","config","key","env","parseEnv","splitBy","separator","value","s"]}
1
+ {"version":3,"sources":["../src/types.ts","../src/env.ts","../src/envBoolean.ts","../src/errors.ts","../src/parseEnv.ts","../src/parseEnvFlat.ts","../src/splitBy.ts"],"sourcesContent":["import type { InferType, SchemaBuilder } from '@cleverbrush/schema';\n\n/**\n * Brand symbol used to distinguish EnvField from plain objects at the type level.\n */\nexport const ENV_FIELD_BRAND: unique symbol = Symbol('ENV_FIELD_BRAND');\n\n/**\n * A branded wrapper around a schema builder that associates it with an\n * environment variable name. Created exclusively by the {@link env} function.\n */\nexport type EnvField<T extends SchemaBuilder<any, any, any, any, any>> = {\n readonly [ENV_FIELD_BRAND]: true;\n readonly varName: string;\n readonly schema: T;\n};\n\n/**\n * A node in the config descriptor tree.\n * - Leaf: an `EnvField` (created via `env()`)\n * - Branch: a plain object whose values are themselves `EnvConfigNode`s\n *\n * Schema builders without `env()` wrapping intentionally fail to satisfy\n * this type, producing a compile-time error.\n */\nexport type EnvConfigNode =\n | EnvField<any>\n | { readonly [key: string]: EnvConfigNode };\n\n/**\n * Top-level config descriptor: a record of `EnvConfigNode`s.\n */\nexport type EnvConfig = Record<string, EnvConfigNode>;\n\n/**\n * Recursively infers the runtime type from an `EnvConfig` descriptor tree.\n *\n * - `EnvField<S>` leaves resolve to `InferType<S>`\n * - Object branches resolve to `{ [K]: InferEnvConfig<V> }`\n */\nexport type InferEnvConfig<T> = {\n [K in keyof T]: T[K] extends EnvField<infer S>\n ? InferType<S>\n : T[K] extends Record<string, any>\n ? InferEnvConfig<T[K]>\n : never;\n};\n","import type { SchemaBuilder } from '@cleverbrush/schema';\nimport { ENV_FIELD_BRAND, type EnvField } from './types.js';\n\n/**\n * Associates a schema builder with an environment variable name.\n *\n * Every leaf field in a config descriptor passed to `parseEnv()` must be\n * wrapped with `env()`. This is enforced at the TypeScript level — passing\n * a bare schema builder produces a compile-time error.\n *\n * @param varName - The environment variable name to read (e.g. `'DB_HOST'`).\n * @param schema - A `@cleverbrush/schema` builder describing the expected\n * type, constraints, coercion, and defaults.\n * @returns A branded `EnvField` descriptor.\n *\n * @example\n * ```ts\n * env('DB_PORT', number().coerce().default(5432))\n * ```\n */\nexport function env<T extends SchemaBuilder<any, any, any, any, any>>(\n varName: string,\n schema: T\n): EnvField<T> {\n if (typeof varName !== 'string' || !varName) {\n throw new Error('env(): varName must be a non-empty string');\n }\n return {\n [ENV_FIELD_BRAND]: true,\n varName,\n schema\n } as EnvField<T>;\n}\n","import { boolean } from '@cleverbrush/schema';\n\n/**\n * Options for {@link envBoolean}.\n */\nexport interface EnvBooleanOptions {\n /**\n * String values accepted as `true`.\n *\n * @defaultValue `['true', '1', 'yes', 'on']`\n */\n trueValues?: readonly string[];\n\n /**\n * String values accepted as `false`.\n *\n * @defaultValue `['false', '0', 'no', 'off']`\n */\n falseValues?: readonly string[];\n\n /**\n * Whether string matching is case-sensitive.\n *\n * @defaultValue `false`\n */\n caseSensitive?: boolean;\n\n /**\n * Whether to trim surrounding whitespace before matching.\n *\n * @defaultValue `true`\n */\n trim?: boolean;\n}\n\nconst DEFAULT_TRUE_VALUES = ['true', '1', 'yes', 'on'] as const;\nconst DEFAULT_FALSE_VALUES = ['false', '0', 'no', 'off'] as const;\n\n/**\n * Create a boolean schema tuned for environment variables.\n *\n * The base schema `boolean().coerce()` intentionally accepts only\n * `\"true\"`/`\"false\"`. Environment variables often use shell-style toggles\n * such as `1`, `0`, `yes`, `no`, `on`, and `off`, so this helper normalizes\n * those values before boolean validation runs.\n *\n * @param options - Matching behavior and accepted true/false strings.\n * @returns A boolean schema builder with env-style string preprocessing.\n *\n * @example\n * ```ts\n * const config = parseEnv({\n * debug: env('DEBUG', envBoolean().default(false)),\n * });\n * ```\n */\nexport function envBoolean(options: EnvBooleanOptions = {}) {\n const {\n trueValues = DEFAULT_TRUE_VALUES,\n falseValues = DEFAULT_FALSE_VALUES,\n caseSensitive = false,\n trim = true\n } = options;\n\n const normalize = (value: string): string => {\n const trimmed = trim ? value.trim() : value;\n return caseSensitive ? trimmed : trimmed.toLowerCase();\n };\n\n const trueSet = new Set(trueValues.map(normalize));\n const falseSet = new Set(falseValues.map(normalize));\n\n return boolean().addPreprocessor(\n value => {\n if (typeof value !== 'string') return value;\n\n const normalized = normalize(value);\n if (trueSet.has(normalized)) return true;\n if (falseSet.has(normalized)) return false;\n\n return value;\n },\n { mutates: true }\n );\n}\n","/**\n * Describes a single missing environment variable.\n */\nexport interface MissingEnvVar {\n /** The environment variable name (e.g. `'DB_HOST'`). */\n varName: string;\n /** Dot-separated config path (e.g. `'db.host'`). */\n configPath: string;\n /** Schema type identifier (e.g. `'string'`, `'number'`). */\n type: string;\n}\n\n/**\n * Describes a single invalid environment variable.\n */\nexport interface InvalidEnvVar {\n /** The environment variable name (e.g. `'DB_PORT'`). */\n varName: string;\n /** Dot-separated config path (e.g. `'db.port'`). */\n configPath: string;\n /** The raw string value that failed validation. */\n value: string;\n /** Validation error messages. */\n errors: string[];\n}\n\n/**\n * Thrown by `parseEnv()` when one or more environment variables are missing\n * or fail validation.\n *\n * The structured `missing` and `invalid` properties allow programmatic\n * inspection, while the formatted `message` provides a human-readable\n * summary suitable for CI logs and startup output.\n */\nexport class EnvValidationError extends Error {\n public readonly missing: readonly MissingEnvVar[];\n public readonly invalid: readonly InvalidEnvVar[];\n\n constructor(missing: MissingEnvVar[], invalid: InvalidEnvVar[]) {\n const lines: string[] = [];\n\n if (missing.length > 0) {\n lines.push('Missing environment variables:');\n for (const m of missing) {\n lines.push(\n ` - ${m.varName} (required by ${m.configPath}) [${m.type}]`\n );\n }\n }\n\n if (invalid.length > 0) {\n lines.push('Invalid environment variables:');\n for (const inv of invalid) {\n lines.push(\n ` - ${inv.varName}: ${JSON.stringify(inv.value)} (required by ${inv.configPath}) — ${inv.errors.join('; ')}`\n );\n }\n }\n\n super(lines.join('\\n'));\n this.name = 'EnvValidationError';\n this.missing = missing;\n this.invalid = invalid;\n }\n}\n","import { deepExtend, type Merge } from '@cleverbrush/deep';\nimport { object, type SchemaBuilder } from '@cleverbrush/schema';\nimport {\n EnvValidationError,\n type InvalidEnvVar,\n type MissingEnvVar\n} from './errors.js';\nimport {\n ENV_FIELD_BRAND,\n type EnvConfig,\n type EnvField,\n type InferEnvConfig\n} from './types.js';\n\n/**\n * Returns `true` if the value is an `EnvField` (created by `env()`).\n */\nfunction isEnvField(value: unknown): value is EnvField<any> {\n return (\n typeof value === 'object' &&\n value !== null &&\n ENV_FIELD_BRAND in value &&\n (value as any)[ENV_FIELD_BRAND] === true\n );\n}\n\n/**\n * Metadata collected during the config tree walk, tracking which env var\n * maps to which config path for error reporting.\n */\ninterface EnvMapping {\n varName: string;\n configPath: string;\n schema: SchemaBuilder<any, any, any, any, any>;\n}\n\n/**\n * Recursively walks the config descriptor tree:\n * - For `EnvField` leaves: reads `source[varName]` and places the raw value\n * - For object branches: recurses into children\n *\n * @returns `rawObject` — a nested object with raw string values at the\n * correct paths, and `mappings` — an array tracking varName→configPath\n * for error reporting.\n */\nfunction walkConfig(\n config: Record<string, unknown>,\n source: Record<string, string | undefined>,\n pathPrefix: string\n): { rawObject: Record<string, unknown>; mappings: EnvMapping[] } {\n const rawObject: Record<string, unknown> = {};\n const mappings: EnvMapping[] = [];\n\n for (const key of Object.keys(config)) {\n const node = config[key];\n const configPath = pathPrefix ? `${pathPrefix}.${key}` : key;\n\n if (isEnvField(node)) {\n const raw = source[node.varName];\n rawObject[key] = raw === '' ? undefined : raw;\n mappings.push({\n varName: node.varName,\n configPath,\n schema: node.schema\n });\n } else if (typeof node === 'object' && node !== null) {\n const child = walkConfig(\n node as Record<string, unknown>,\n source,\n configPath\n );\n rawObject[key] = child.rawObject;\n mappings.push(...child.mappings);\n }\n }\n\n return { rawObject, mappings };\n}\n\n/**\n * Recursively builds an `ObjectSchemaBuilder` from the config descriptor tree.\n *\n * For `EnvField` leaves the inner schema is used directly.\n * For object branches a nested `object()` schema is constructed.\n */\nfunction buildSchema(\n config: Record<string, unknown>\n): SchemaBuilder<any, any, any, any, any> {\n const props: Record<string, SchemaBuilder<any, any, any, any, any>> = {};\n\n for (const key of Object.keys(config)) {\n const node = config[key];\n\n if (isEnvField(node)) {\n props[key] = node.schema;\n } else if (typeof node === 'object' && node !== null) {\n props[key] = buildSchema(node as Record<string, unknown>);\n }\n }\n\n return object(props as any) as any;\n}\n\n/**\n * Parses environment variables into a validated, typed config object.\n *\n * Every leaf field in the config descriptor must be wrapped with `env()` —\n * this is enforced at the TypeScript level.\n *\n * The function:\n * 1. Walks the descriptor tree, reading raw values from `source` (defaults\n * to `process.env`).\n * 2. Assembles a `@cleverbrush/schema` object schema from the leaf schemas.\n * 3. Validates and coerces the raw values via `schema.validate()`.\n * 4. Returns the typed result or throws an `EnvValidationError` listing\n * all missing and invalid variables.\n *\n * @param config - A descriptor tree where leaves are `EnvField`s and\n * branches are plain objects.\n * @param source - The environment variable source. Defaults to `process.env`.\n *\n * @example\n * ```ts\n * const config = parseEnv({\n * db: {\n * host: env('DB_HOST', string().default('localhost')),\n * port: env('DB_PORT', number().coerce().default(5432)),\n * },\n * debug: env('DEBUG', boolean().coerce().default(false)),\n * });\n * ```\n */\nexport function parseEnv<T extends EnvConfig>(\n config: T,\n source?: Record<string, string | undefined>\n): InferEnvConfig<T>;\nexport function parseEnv<\n T extends EnvConfig,\n C extends Record<string, unknown>\n>(\n config: T,\n compute: (base: InferEnvConfig<T>) => C,\n source?: Record<string, string | undefined>\n): Merge<[InferEnvConfig<T>, C]>;\nexport function parseEnv<T extends EnvConfig>(\n config: T,\n computeOrSource?:\n | ((base: InferEnvConfig<T>) => Record<string, unknown>)\n | Record<string, string | undefined>,\n maybeSource?: Record<string, string | undefined>\n): unknown {\n const isCompute = typeof computeOrSource === 'function';\n const source: Record<string, string | undefined> = isCompute\n ? (maybeSource ?? (typeof process !== 'undefined' ? process.env : {}))\n : (computeOrSource ??\n (typeof process !== 'undefined' ? process.env : {}));\n\n const { rawObject, mappings } = walkConfig(config, source, '');\n const schema = buildSchema(config);\n\n const result = schema.validate(rawObject, {\n doNotStopOnFirstError: true\n });\n\n if (result.valid) {\n const base = result.object as InferEnvConfig<T>;\n if (isCompute) {\n const computed = (\n computeOrSource as (\n base: InferEnvConfig<T>\n ) => Record<string, unknown>\n )(base);\n return deepExtend(base, computed);\n }\n return base;\n }\n\n // Map schema validation errors back to env var names\n const missing: MissingEnvVar[] = [];\n const invalid: InvalidEnvVar[] = [];\n\n const errorMessages = result.errors?.map(e => e.message) ?? [];\n\n // For each mapping, check if the var was present and if there's an error\n for (const mapping of mappings) {\n const raw = source[mapping.varName];\n const introspected = mapping.schema.introspect();\n const isRequired = introspected.isRequired;\n const hasDefault = introspected.hasDefault;\n\n if (raw === undefined || raw === '') {\n if (isRequired && !hasDefault) {\n missing.push({\n varName: mapping.varName,\n configPath: mapping.configPath,\n type: introspected.type\n });\n }\n } else {\n // Validate this individual field to see if it's invalid\n const fieldResult = mapping.schema.validate(raw);\n if (!fieldResult.valid) {\n invalid.push({\n varName: mapping.varName,\n configPath: mapping.configPath,\n value: raw,\n errors: fieldResult.errors?.map(e => e.message) ?? []\n });\n }\n }\n }\n\n // If we couldn't categorize any errors but validation still failed,\n // add the raw error messages as a generic invalid entry\n if (\n missing.length === 0 &&\n invalid.length === 0 &&\n errorMessages.length > 0\n ) {\n invalid.push({\n varName: '(unknown)',\n configPath: '(unknown)',\n value: '',\n errors: errorMessages\n });\n }\n\n throw new EnvValidationError(missing, invalid);\n}\n","import type { InferType, SchemaBuilder } from '@cleverbrush/schema';\nimport { env } from './env.js';\nimport { parseEnv } from './parseEnv.js';\n\n/**\n * Flat schema map: keys are environment variable names, values are schema builders.\n */\ntype FlatEnvSchemas = Record<string, SchemaBuilder<any, any, any, any, any>>;\n\n/**\n * Infers the runtime type from a flat schema map.\n */\ntype InferFlatEnv<T extends FlatEnvSchemas> = {\n [K in keyof T]: InferType<T[K]>;\n};\n\n/**\n * Convenience wrapper for simple flat configs where each key is both the\n * config property name and the environment variable name.\n *\n * Equivalent to calling `parseEnv()` with every entry wrapped in `env()`.\n *\n * @param schemas - A record mapping env var names to schema builders.\n * @param source - The environment variable source. Defaults to `process.env`.\n *\n * @example\n * ```ts\n * const config = parseEnvFlat({\n * DB_HOST: string().default('localhost'),\n * DB_PORT: number().coerce().default(5432),\n * JWT_SECRET: string().minLength(32),\n * });\n * // Type: { DB_HOST: string, DB_PORT: number, JWT_SECRET: string }\n * ```\n */\nexport function parseEnvFlat<T extends FlatEnvSchemas>(\n schemas: T,\n source?: Record<string, string | undefined>\n): InferFlatEnv<T> {\n const config: Record<string, ReturnType<typeof env>> = {};\n for (const key of Object.keys(schemas)) {\n config[key] = env(key, schemas[key]);\n }\n return parseEnv(config as any, source) as InferFlatEnv<T>;\n}\n","/**\n * Creates a preprocessor function that splits a string value by the given\n * separator and trims each resulting element.\n *\n * Intended for use with `array()` schemas to parse comma-separated (or\n * similarly delimited) environment variable values.\n *\n * @param separator - The delimiter string (e.g. `','`, `';'`, `' '`).\n * @returns A preprocessor function suitable for `schema.addPreprocessor()`.\n *\n * @example\n * ```ts\n * env('ALLOWED_ORIGINS', array(string()).addPreprocessor(splitBy(','), { mutates: false }))\n * // \"a, b, c\" → ['a', 'b', 'c']\n * ```\n */\nexport function splitBy<T = string>(separator: string): (value: T[]) => T[] {\n return ((value: unknown): unknown => {\n if (typeof value === 'string') {\n return value.split(separator).map(s => s.trim());\n }\n return value;\n }) as (value: T[]) => T[];\n}\n"],"mappings":"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","names":["ENV_FIELD_BRAND","env","varName","schema","ENV_FIELD_BRAND","boolean","DEFAULT_TRUE_VALUES","DEFAULT_FALSE_VALUES","envBoolean","options","trueValues","falseValues","caseSensitive","trim","normalize","value","trimmed","trueSet","falseSet","normalized","EnvValidationError","missing","invalid","lines","m","inv","deepExtend","object","isEnvField","value","ENV_FIELD_BRAND","walkConfig","config","source","pathPrefix","rawObject","mappings","key","node","configPath","raw","child","buildSchema","props","object","parseEnv","computeOrSource","maybeSource","isCompute","result","base","computed","deepExtend","missing","invalid","errorMessages","e","mapping","introspected","isRequired","hasDefault","fieldResult","EnvValidationError","parseEnvFlat","schemas","source","config","key","env","parseEnv","splitBy","separator","value","s"]}
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "email": "andrew_zol@cleverbrush.com"
6
6
  },
7
7
  "dependencies": {
8
- "@cleverbrush/deep": "^4.3.2"
8
+ "@cleverbrush/deep": "^4.4.1"
9
9
  },
10
10
  "peerDependencies": {
11
11
  "@cleverbrush/schema": "^4.0.0"
@@ -50,5 +50,5 @@
50
50
  "@types/node": "^25.4.0"
51
51
  },
52
52
  "types": "./dist/index.d.ts",
53
- "version": "4.3.2"
53
+ "version": "4.4.1"
54
54
  }