konfeeg 0.0.1 → 0.0.2
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 +1 -1
- package/dist/index.cjs +6 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -6
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +21 -6
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +6 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -102,7 +102,7 @@ config.mongo.poolSize // number
|
|
|
102
102
|
| Field | Required | Description |
|
|
103
103
|
| --------------------------------- | -------- | ------------------------------------------------------------------------------------- |
|
|
104
104
|
| `doc` | required | Human-readable description |
|
|
105
|
-
| `format` |
|
|
105
|
+
| `format` | optional | Validation format — see below. If omitted, no validation is applied; the resolved type is inferred from `value`/per-env fields, or `any` if none are declared |
|
|
106
106
|
| `value` | optional | Constant shared across all environments (lowest priority) |
|
|
107
107
|
| `processEnv` | optional | `process.env` key — runtime override (highest priority) |
|
|
108
108
|
| `importMetaEnv` | optional | `import.meta.env` key — runtime override (highest priority) |
|
package/dist/index.cjs
CHANGED
|
@@ -3,28 +3,28 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
3
3
|
function validateAndCoerce(value, format, fullKey, errors) {
|
|
4
4
|
switch (format) {
|
|
5
5
|
case String:
|
|
6
|
-
if (typeof value !== "string") errors.push(
|
|
6
|
+
if (typeof value !== "string") errors.push(`${fullKey}: must be a string`);
|
|
7
7
|
break;
|
|
8
8
|
case Number:
|
|
9
9
|
value = Number(value);
|
|
10
|
-
if (isNaN(value)) errors.push(
|
|
10
|
+
if (isNaN(value)) errors.push(`${fullKey}: must be a number`);
|
|
11
11
|
break;
|
|
12
12
|
case Boolean:
|
|
13
|
-
if (typeof value !== "boolean" && value !== "true" && value !== "false" && value !== 1 && value !== 0) errors.push(
|
|
13
|
+
if (typeof value !== "boolean" && value !== "true" && value !== "false" && value !== 1 && value !== 0) errors.push(`${fullKey}: must be a boolean`);
|
|
14
14
|
value = value === "true" ? true : value === "false" ? false : Boolean(value);
|
|
15
15
|
break;
|
|
16
16
|
case Array:
|
|
17
|
-
if (!Array.isArray(value)) errors.push(
|
|
17
|
+
if (!Array.isArray(value)) errors.push(`${fullKey}: must be an array`);
|
|
18
18
|
break;
|
|
19
19
|
case "url":
|
|
20
20
|
try {
|
|
21
21
|
new URL(value);
|
|
22
22
|
} catch {
|
|
23
|
-
errors.push(
|
|
23
|
+
errors.push(`${fullKey}: must be a valid URL; found "${value}"`);
|
|
24
24
|
}
|
|
25
25
|
break;
|
|
26
26
|
default: if (format instanceof Array) {
|
|
27
|
-
if (!format.includes(value)) errors.push(
|
|
27
|
+
if (!format.includes(value)) errors.push(`${fullKey}: must be one of: [${format.join(", ")}]`);
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
return value;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../src/format.ts","../src/create-config.ts","../src/define-config.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\n\nexport function validateAndCoerce(\n value: any,\n format: any,\n fullKey: string,\n errors: string[],\n): any {\n switch (format) {\n case String:\n if (typeof value !== \"string\") {\n errors.push(`Config value for ${fullKey} must be a string`)\n }\n break\n case Number:\n value = Number(value)\n if (isNaN(value))\n errors.push(`Config value for ${fullKey} must be a number`)\n break\n case Boolean:\n if (\n typeof value !== \"boolean\" &&\n value !== \"true\" &&\n value !== \"false\" &&\n value !== 1 &&\n value !== 0\n ) {\n errors.push(`Config value for ${fullKey} must be a boolean`)\n }\n value =\n value === \"true\" ? true : value === \"false\" ? false : Boolean(value)\n break\n case Array:\n if (!Array.isArray(value)) {\n errors.push(`Config value for ${fullKey} must be an array`)\n }\n break\n case \"url\":\n try {\n new URL(value)\n } catch {\n errors.push(\n `Config value for ${fullKey} must be a valid URL; found \"${value}\"`,\n )\n }\n break\n default:\n if (format instanceof Array) {\n if (!format.includes(value)) {\n errors.push(\n `Config value for ${fullKey} must be one of: [${format.join(\", \")}]`,\n )\n }\n }\n }\n\n return value\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { validateAndCoerce } from \"./format.js\"\nimport type {\n ConfigGroup,\n ResolveConfigGroup,\n ValidateSchema,\n} from \"./types.js\"\nimport type {\n CreateConfigOptions,\n EnvName,\n EnvsShape,\n Fallbacks,\n} from \"./util-types.js\"\n\n/**\n * Create a resolved, validated config for the given environment.\n *\n * Curried so the envs declaration is bound on the first call and the\n * schema is inferred (giving you autocomplete) on the second call.\n *\n * @typeParam E - The envs shape describing required/optional environments.\n *\n * @example\n * ```ts\n * type MyEnvs = {\n * dev?: unknown\n * staging: unknown\n * production: unknown\n * }\n * const config = createEnvironmentConfig<MyEnvs>()('dev', {\n * port: { doc: 'Port', format: Number, value: 3000 },\n * })\n * config.port // number\n * ```\n *\n * @example Fallback environments\n * ```ts\n * // When running in `dev`, any entry that does not declare a `dev` value\n * // falls back to the entry's `integ` value.\n * const config = createEnvironmentConfig<MyEnvs>()(\n * 'dev',\n * {\n * apiUrl: {\n * doc: 'API URL',\n * format: 'url',\n * integ: 'https://integ.example.com',\n * staging: 'https://staging.example.com',\n * production: 'https://api.example.com',\n * },\n * },\n * { fallbacks: { dev: 'integ' } },\n * )\n * ```\n */\nexport function createEnvironmentConfig<E extends EnvsShape>() {\n return <const G extends ConfigGroup<E>>(\n env: EnvName<E>,\n inputConfig: ValidateSchema<G, E>,\n options?: CreateConfigOptions<E>,\n ): ResolveConfigGroup<G> & { env: EnvName<E> } =>\n buildConfig<E, G>(env, inputConfig as unknown as G, options)\n}\n\nfunction buildConfig<E extends EnvsShape, G extends ConfigGroup<E>>(\n env: EnvName<E>,\n inputConfig: G,\n options?: CreateConfigOptions<E>,\n): ResolveConfigGroup<G> & { env: EnvName<E> } {\n const errors: string[] = []\n\n // Resolve the per-environment lookup chain once for the active env.\n // Throws synchronously on a circular fallback chain.\n const envChain = resolveFallbackChain<E>(env, options?.fallbacks)\n\n function processConfig(\n config: ConfigGroup<E>,\n keyPrefix: string,\n ): Record<string, any> {\n const output: Record<string, any> = {}\n\n for (const [key, entry] of Object.entries(config)) {\n if (key === \"env\") {\n throw new Error(\n `Config key \"env\" is reserved and cannot be used. It will already be present by default.`,\n )\n }\n\n const fullKey = keyPrefix ? `${keyPrefix}.${key}` : key\n\n if (!(\"doc\" in entry)) {\n output[key] = processConfig(entry as ConfigGroup<E>, fullKey)\n continue\n }\n\n const configEntry = entry as any\n\n // Value resolution — sources are evaluated in ascending priority order.\n // The highest-priority source that resolves to a defined value wins.\n //\n // Priority │ Source\n // ─────────┼────────────────────────────────────────────────────────────────\n // 1 (low) │ Static `value` — same value across all environments\n // 2 │ Per-environment field, walking the fallback chain\n // │ — overrides the static value for that specific environment\n // 3 (high) │ Runtime env var via `processEnv` or `importMetaEnv`\n // │ — always wins; intended for secrets and local dev overrides\n\n // Priority 1: static value (lowest precedence)\n let value: any = \"value\" in configEntry ? configEntry.value : undefined\n\n // Priority 2: per-environment value, walking the fallback chain.\n // The first env in the chain with a defined value wins.\n for (const candidateEnv of envChain) {\n const envValue = configEntry[candidateEnv]\n if (envValue !== undefined) {\n value = envValue\n break\n }\n }\n\n // Priority 3: runtime env var (highest precedence — always wins when defined)\n if (\"processEnv\" in configEntry) {\n const runtimeOverride =\n // @ts-expect-error process may not be defined in browser builds\n typeof process !== \"undefined\" && process.env\n ? // @ts-expect-error process may not be defined in browser builds\n process.env[configEntry.processEnv as string]\n : undefined\n if (runtimeOverride !== undefined) value = runtimeOverride\n } else if (\"importMetaEnv\" in configEntry) {\n const runtimeOverride =\n // @ts-expect-error import.meta.env may not be defined in Node builds\n typeof import.meta !== \"undefined\" && import.meta.env\n ? // @ts-expect-error import.meta.env may not be defined in Node builds\n import.meta.env[configEntry.importMetaEnv as string]\n : undefined\n if (runtimeOverride !== undefined) value = runtimeOverride\n }\n\n const hasValueSource =\n value !== undefined ||\n \"processEnv\" in configEntry ||\n \"importMetaEnv\" in configEntry ||\n configEntry.optional\n\n if (value === undefined && !hasValueSource) {\n errors.push(\n `${fullKey}: No value source declared and \"optional\" is not set.`,\n )\n continue\n }\n\n if (value === undefined) {\n if (configEntry.optional) {\n value = configEntry.default\n if (value === undefined) {\n output[key] = undefined\n continue\n }\n } else {\n errors.push(\n `${fullKey}: Missing required config value in environment ${env}`,\n )\n continue\n }\n }\n\n //\n // Format validation and coercion\n //\n value = validateAndCoerce(value, configEntry.format, fullKey, errors)\n\n output[key] = value\n }\n\n return output\n }\n\n let outputConfig = processConfig(inputConfig, \"\")\n\n if (errors.length > 0) {\n console.error(\"Environment config validation failed\", errors)\n throw new Error(\n `Environment config validation failed:\\n${errors.join(\"\\n\")}`,\n )\n }\n\n outputConfig = {\n env,\n ...outputConfig,\n }\n return outputConfig as ResolveConfigGroup<G> & { env: EnvName<E> }\n}\n\n/**\n * Build the ordered list of environments to consult for per-environment\n * value resolution. The active env is always first; each subsequent entry\n * is the fallback target declared for the previous env. Throws if the\n * chain is cyclic.\n */\nfunction resolveFallbackChain<E extends EnvsShape>(\n env: EnvName<E>,\n fallbacks: Fallbacks<E> | undefined,\n): EnvName<E>[] {\n const chain: EnvName<E>[] = [env]\n if (!fallbacks) return chain\n\n const seen = new Set<string>([env])\n let current: EnvName<E> = env\n while (fallbacks[current] !== undefined) {\n const next = fallbacks[current] as EnvName<E>\n if (seen.has(next)) {\n throw new Error(\n `Circular fallback chain detected: ${[...chain, next].join(\" -> \")}`,\n )\n }\n seen.add(next)\n chain.push(next)\n current = next\n }\n return chain\n}\n","import { createEnvironmentConfig } from \"./create-config.js\"\nimport type {\n ConfigGroup,\n ResolveConfigGroup,\n ValidateSchema,\n} from \"./types.js\"\nimport type { CreateConfigOptions, EnvName, EnvsShape } from \"./util-types.js\"\n\n/**\n * Like {@link createEnvironmentConfig}, but binds the schema first and the\n * environment later. Useful when the active environment is not known at\n * schema-definition time.\n *\n * @typeParam E - The envs shape describing required/optional environments.\n *\n * @example\n * ```ts\n * type MyEnvs = {\n * dev?: unknown\n * staging: unknown\n * production: unknown\n * }\n * const buildConfig = defineEnvironmentConfig<MyEnvs>()({\n * port: { doc: 'Port', format: Number, value: 3000 },\n * })\n * const config = buildConfig('dev')\n * ```\n *\n * @example Fallback environments\n * ```ts\n * const buildConfig = defineEnvironmentConfig<MyEnvs>()(\n * { apiUrl: { doc: 'API URL', format: 'url', staging: 'https://staging' } },\n * { fallbacks: { dev: 'staging' } },\n * )\n * const config = buildConfig('dev') // apiUrl resolved from `staging`\n * ```\n */\nexport function defineEnvironmentConfig<E extends EnvsShape>() {\n const create = createEnvironmentConfig<E>()\n return <const G extends ConfigGroup<E>>(\n inputConfig: ValidateSchema<G, E>,\n options?: CreateConfigOptions<E>,\n ): ((env: EnvName<E>) => ResolveConfigGroup<G> & { env: EnvName<E> }) =>\n (env: EnvName<E>) =>\n create(env, inputConfig as any, options)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../src/format.ts","../src/create-config.ts","../src/define-config.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\n\nexport function validateAndCoerce(\n value: any,\n format: any,\n fullKey: string,\n errors: string[],\n): any {\n switch (format) {\n case String:\n if (typeof value !== \"string\") {\n errors.push(`${fullKey}: must be a string`)\n }\n break\n case Number:\n value = Number(value)\n if (isNaN(value))\n errors.push(`${fullKey}: must be a number`)\n break\n case Boolean:\n if (\n typeof value !== \"boolean\" &&\n value !== \"true\" &&\n value !== \"false\" &&\n value !== 1 &&\n value !== 0\n ) {\n errors.push(`${fullKey}: must be a boolean`)\n }\n value =\n value === \"true\" ? true : value === \"false\" ? false : Boolean(value)\n break\n case Array:\n if (!Array.isArray(value)) {\n errors.push(`${fullKey}: must be an array`)\n }\n break\n case \"url\":\n try {\n new URL(value)\n } catch {\n errors.push(\n `${fullKey}: must be a valid URL; found \"${value}\"`,\n )\n }\n break\n default:\n if (format instanceof Array) {\n if (!format.includes(value)) {\n errors.push(\n `${fullKey}: must be one of: [${format.join(\", \")}]`,\n )\n }\n }\n }\n\n return value\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { validateAndCoerce } from \"./format.js\"\nimport type {\n ConfigGroup,\n ResolveConfigGroup,\n ValidateSchema,\n} from \"./types.js\"\nimport type {\n CreateConfigOptions,\n EnvName,\n EnvsShape,\n Fallbacks,\n} from \"./util-types.js\"\n\n/**\n * Create a resolved, validated config for the given environment.\n *\n * Curried so the envs declaration is bound on the first call and the\n * schema is inferred (giving you autocomplete) on the second call.\n *\n * @typeParam E - The envs shape describing required/optional environments.\n *\n * @example\n * ```ts\n * type MyEnvs = {\n * dev?: unknown\n * staging: unknown\n * production: unknown\n * }\n * const config = createEnvironmentConfig<MyEnvs>()('dev', {\n * port: { doc: 'Port', format: Number, value: 3000 },\n * })\n * config.port // number\n * ```\n *\n * @example Fallback environments\n * ```ts\n * // When running in `dev`, any entry that does not declare a `dev` value\n * // falls back to the entry's `integ` value.\n * const config = createEnvironmentConfig<MyEnvs>()(\n * 'dev',\n * {\n * apiUrl: {\n * doc: 'API URL',\n * format: 'url',\n * integ: 'https://integ.example.com',\n * staging: 'https://staging.example.com',\n * production: 'https://api.example.com',\n * },\n * },\n * { fallbacks: { dev: 'integ' } },\n * )\n * ```\n */\nexport function createEnvironmentConfig<E extends EnvsShape>() {\n return <const G extends ConfigGroup<E>>(\n env: EnvName<E>,\n inputConfig: ValidateSchema<G, E>,\n options?: CreateConfigOptions<E>,\n ): ResolveConfigGroup<G> & { env: EnvName<E> } =>\n buildConfig<E, G>(env, inputConfig as unknown as G, options)\n}\n\nfunction buildConfig<E extends EnvsShape, G extends ConfigGroup<E>>(\n env: EnvName<E>,\n inputConfig: G,\n options?: CreateConfigOptions<E>,\n): ResolveConfigGroup<G> & { env: EnvName<E> } {\n const errors: string[] = []\n\n // Resolve the per-environment lookup chain once for the active env.\n // Throws synchronously on a circular fallback chain.\n const envChain = resolveFallbackChain<E>(env, options?.fallbacks)\n\n function processConfig(\n config: ConfigGroup<E>,\n keyPrefix: string,\n ): Record<string, any> {\n const output: Record<string, any> = {}\n\n for (const [key, entry] of Object.entries(config)) {\n if (key === \"env\") {\n throw new Error(\n `Config key \"env\" is reserved and cannot be used. It will already be present by default.`,\n )\n }\n\n const fullKey = keyPrefix ? `${keyPrefix}.${key}` : key\n\n if (!(\"doc\" in entry)) {\n output[key] = processConfig(entry as ConfigGroup<E>, fullKey)\n continue\n }\n\n const configEntry = entry as any\n\n // Value resolution — sources are evaluated in ascending priority order.\n // The highest-priority source that resolves to a defined value wins.\n //\n // Priority │ Source\n // ─────────┼────────────────────────────────────────────────────────────────\n // 1 (low) │ Static `value` — same value across all environments\n // 2 │ Per-environment field, walking the fallback chain\n // │ — overrides the static value for that specific environment\n // 3 (high) │ Runtime env var via `processEnv` or `importMetaEnv`\n // │ — always wins; intended for secrets and local dev overrides\n\n // Priority 1: static value (lowest precedence)\n let value: any = \"value\" in configEntry ? configEntry.value : undefined\n\n // Priority 2: per-environment value, walking the fallback chain.\n // The first env in the chain with a defined value wins.\n for (const candidateEnv of envChain) {\n const envValue = configEntry[candidateEnv]\n if (envValue !== undefined) {\n value = envValue\n break\n }\n }\n\n // Priority 3: runtime env var (highest precedence — always wins when defined)\n if (\"processEnv\" in configEntry) {\n const runtimeOverride =\n // @ts-expect-error process may not be defined in browser builds\n typeof process !== \"undefined\" && process.env\n ? // @ts-expect-error process may not be defined in browser builds\n process.env[configEntry.processEnv as string]\n : undefined\n if (runtimeOverride !== undefined) value = runtimeOverride\n } else if (\"importMetaEnv\" in configEntry) {\n const runtimeOverride =\n // @ts-expect-error import.meta.env may not be defined in Node builds\n typeof import.meta !== \"undefined\" && import.meta.env\n ? // @ts-expect-error import.meta.env may not be defined in Node builds\n import.meta.env[configEntry.importMetaEnv as string]\n : undefined\n if (runtimeOverride !== undefined) value = runtimeOverride\n }\n\n const hasValueSource =\n value !== undefined ||\n \"processEnv\" in configEntry ||\n \"importMetaEnv\" in configEntry ||\n configEntry.optional\n\n if (value === undefined && !hasValueSource) {\n errors.push(\n `${fullKey}: No value source declared and \"optional\" is not set.`,\n )\n continue\n }\n\n if (value === undefined) {\n if (configEntry.optional) {\n value = configEntry.default\n if (value === undefined) {\n output[key] = undefined\n continue\n }\n } else {\n errors.push(\n `${fullKey}: Missing required config value in environment ${env}`,\n )\n continue\n }\n }\n\n //\n // Format validation and coercion\n //\n value = validateAndCoerce(value, configEntry.format, fullKey, errors)\n\n output[key] = value\n }\n\n return output\n }\n\n let outputConfig = processConfig(inputConfig, \"\")\n\n if (errors.length > 0) {\n console.error(\"Environment config validation failed\", errors)\n throw new Error(\n `Environment config validation failed:\\n${errors.join(\"\\n\")}`,\n )\n }\n\n outputConfig = {\n env,\n ...outputConfig,\n }\n return outputConfig as ResolveConfigGroup<G> & { env: EnvName<E> }\n}\n\n/**\n * Build the ordered list of environments to consult for per-environment\n * value resolution. The active env is always first; each subsequent entry\n * is the fallback target declared for the previous env. Throws if the\n * chain is cyclic.\n */\nfunction resolveFallbackChain<E extends EnvsShape>(\n env: EnvName<E>,\n fallbacks: Fallbacks<E> | undefined,\n): EnvName<E>[] {\n const chain: EnvName<E>[] = [env]\n if (!fallbacks) return chain\n\n const seen = new Set<string>([env])\n let current: EnvName<E> = env\n while (fallbacks[current] !== undefined) {\n const next = fallbacks[current] as EnvName<E>\n if (seen.has(next)) {\n throw new Error(\n `Circular fallback chain detected: ${[...chain, next].join(\" -> \")}`,\n )\n }\n seen.add(next)\n chain.push(next)\n current = next\n }\n return chain\n}\n","import { createEnvironmentConfig } from \"./create-config.js\"\nimport type {\n ConfigGroup,\n ResolveConfigGroup,\n ValidateSchema,\n} from \"./types.js\"\nimport type { CreateConfigOptions, EnvName, EnvsShape } from \"./util-types.js\"\n\n/**\n * Like {@link createEnvironmentConfig}, but binds the schema first and the\n * environment later. Useful when the active environment is not known at\n * schema-definition time.\n *\n * @typeParam E - The envs shape describing required/optional environments.\n *\n * @example\n * ```ts\n * type MyEnvs = {\n * dev?: unknown\n * staging: unknown\n * production: unknown\n * }\n * const buildConfig = defineEnvironmentConfig<MyEnvs>()({\n * port: { doc: 'Port', format: Number, value: 3000 },\n * })\n * const config = buildConfig('dev')\n * ```\n *\n * @example Fallback environments\n * ```ts\n * const buildConfig = defineEnvironmentConfig<MyEnvs>()(\n * { apiUrl: { doc: 'API URL', format: 'url', staging: 'https://staging' } },\n * { fallbacks: { dev: 'staging' } },\n * )\n * const config = buildConfig('dev') // apiUrl resolved from `staging`\n * ```\n */\nexport function defineEnvironmentConfig<E extends EnvsShape>() {\n const create = createEnvironmentConfig<E>()\n return <const G extends ConfigGroup<E>>(\n inputConfig: ValidateSchema<G, E>,\n options?: CreateConfigOptions<E>,\n ): ((env: EnvName<E>) => ResolveConfigGroup<G> & { env: EnvName<E> }) =>\n (env: EnvName<E>) =>\n create(env, inputConfig as any, options)\n}\n"],"mappings":"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"}
|
package/dist/index.d.cts
CHANGED
|
@@ -96,6 +96,7 @@ type ValueSourceRequired<T> = {
|
|
|
96
96
|
processEnv?: never;
|
|
97
97
|
importMetaEnv: string;
|
|
98
98
|
} | {
|
|
99
|
+
optional: true;
|
|
99
100
|
value?: T;
|
|
100
101
|
processEnv?: never;
|
|
101
102
|
importMetaEnv?: never;
|
|
@@ -109,19 +110,31 @@ type ConfigEntryBase<T, E extends EnvsShape> = {
|
|
|
109
110
|
type ConfigGroup<E extends EnvsShape> = {
|
|
110
111
|
[key: string]: ConfigEntry<any, E> | ConfigGroup<E>;
|
|
111
112
|
};
|
|
113
|
+
type MaybeOptionalUndefined<E, T> = E extends {
|
|
114
|
+
optional: true;
|
|
115
|
+
} ? E extends {
|
|
116
|
+
default: any;
|
|
117
|
+
} ? T : T | undefined : T;
|
|
118
|
+
type ReservedEntryKeys = "doc" | "format" | "optional" | "default" | "value" | "processEnv" | "importMetaEnv";
|
|
119
|
+
type NeverToAny<T> = [T] extends [never] ? any : T;
|
|
120
|
+
type UntypedResolved<E> = NeverToAny<(E extends {
|
|
121
|
+
value: infer V;
|
|
122
|
+
} ? V : never) | (E extends {
|
|
123
|
+
default: infer D;
|
|
124
|
+
} ? D : never) | (E extends Record<string, any> ? E[Exclude<keyof E, ReservedEntryKeys>] : never)>;
|
|
112
125
|
type ResolveEntryType<E> = E extends {
|
|
113
126
|
format: StringConstructor;
|
|
114
|
-
} ? string : E extends {
|
|
127
|
+
} ? MaybeOptionalUndefined<E, string> : E extends {
|
|
115
128
|
format: NumberConstructor;
|
|
116
|
-
} ? number : E extends {
|
|
129
|
+
} ? MaybeOptionalUndefined<E, number> : E extends {
|
|
117
130
|
format: BooleanConstructor;
|
|
118
|
-
} ? boolean : E extends {
|
|
131
|
+
} ? MaybeOptionalUndefined<E, boolean> : E extends {
|
|
119
132
|
format: 'url';
|
|
120
|
-
} ? string : E extends {
|
|
133
|
+
} ? MaybeOptionalUndefined<E, string> : E extends {
|
|
121
134
|
format: (infer F)[];
|
|
122
|
-
} ? F : E extends {
|
|
135
|
+
} ? MaybeOptionalUndefined<E, F> : E extends {
|
|
123
136
|
format: ArrayConstructor;
|
|
124
|
-
} ? any[] :
|
|
137
|
+
} ? MaybeOptionalUndefined<E, any[]> : MaybeOptionalUndefined<E, UntypedResolved<E>>;
|
|
125
138
|
type ResolveConfigGroup<G> = { [K in keyof G]: G[K] extends {
|
|
126
139
|
doc: string;
|
|
127
140
|
} ? ResolveEntryType<G[K]> : ResolveConfigGroup<G[K]> };
|
|
@@ -158,6 +171,8 @@ type ValidateSchema<G, E extends EnvsShape> = { [K in keyof G]: G[K] extends {
|
|
|
158
171
|
format: readonly (infer V)[];
|
|
159
172
|
} ? { [P in keyof G[K]]: P extends EnvName<E> | "value" | "default" ? G[K][P] extends V | undefined ? G[K][P] : V : G[K][P] } : G[K] extends {
|
|
160
173
|
format: unknown;
|
|
174
|
+
} ? G[K] : G[K] extends {
|
|
175
|
+
doc: string;
|
|
161
176
|
} ? G[K] : ValidateSchema<G[K], E> };
|
|
162
177
|
//#endregion
|
|
163
178
|
//#region src/create-config.d.ts
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/util-types.ts","../src/types.ts","../src/create-config.ts","../src/define-config.ts"],"mappings":";;AAoBA;;;;AAA8B;AAAa;;;;;;;;;;;;;KAA/B,SAAA,GAAY,MAAM;AAAA,KAEzB,YAAA,oBAES,CAAA,gBAAiB,IAAA,CAAK,CAAA,EAAG,CAAA,YAAa,CAAA,SAC5C,CAAA;AAAA,KAGH,YAAA,oBAES,CAAA,gBAAiB,IAAA,CAAK,CAAA,EAAG,CAAA,IAAK,CAAA,iBACpC,CAAA;AANC;AAAA,KAUG,OAAA,WAAkB,SAAA,UAAmB,CAAC;;;;;KAMtC,MAAA,WAAiB,SAAA,eAAwB,YAAA,CAAa,CAAA,IAAK,CAAA,aAC/D,YAAA,CAAa,CAAA,KAAM,CAAA;;;;;;;;;;;AAXlB;AAIT;;;;;;;;AAAkD;AAMlD;;;;;;;KA+BY,SAAA,WAAoB,SAAA,IAAa,OAAA,CAC3C,MAAA,CAAO,OAAA,CAAQ,CAAA,GAAI,OAAA,CAAQ,CAAA;;;;;KAOjB,mBAAA,WAA8B,SAAA;EAvCb;;;;EA4C3B,SAAA,GAAY,SAAA,CAAU,CAAA;AAAA;;;KCjFnB,SAAA,WAAoB,SAAA,YAAqB,OAAA,CAAQ,CAAA;AAAA,KAGjD,qBAAA;EACC,KAAA,GAAQ,CAAA;EAAG,UAAA;EAAoB,aAAA;AAAA;EAC/B,KAAA,GAAQ,CAAA;EAAG,UAAA;EAAoB,aAAA;AAAA;EAC/B,KAAA,GAAQ,CAAA;EAAG,UAAA;EAAoB,aAAA;AAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/util-types.ts","../src/types.ts","../src/create-config.ts","../src/define-config.ts"],"mappings":"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"}
|
package/dist/index.d.mts
CHANGED
|
@@ -96,6 +96,7 @@ type ValueSourceRequired<T> = {
|
|
|
96
96
|
processEnv?: never;
|
|
97
97
|
importMetaEnv: string;
|
|
98
98
|
} | {
|
|
99
|
+
optional: true;
|
|
99
100
|
value?: T;
|
|
100
101
|
processEnv?: never;
|
|
101
102
|
importMetaEnv?: never;
|
|
@@ -109,19 +110,31 @@ type ConfigEntryBase<T, E extends EnvsShape> = {
|
|
|
109
110
|
type ConfigGroup<E extends EnvsShape> = {
|
|
110
111
|
[key: string]: ConfigEntry<any, E> | ConfigGroup<E>;
|
|
111
112
|
};
|
|
113
|
+
type MaybeOptionalUndefined<E, T> = E extends {
|
|
114
|
+
optional: true;
|
|
115
|
+
} ? E extends {
|
|
116
|
+
default: any;
|
|
117
|
+
} ? T : T | undefined : T;
|
|
118
|
+
type ReservedEntryKeys = "doc" | "format" | "optional" | "default" | "value" | "processEnv" | "importMetaEnv";
|
|
119
|
+
type NeverToAny<T> = [T] extends [never] ? any : T;
|
|
120
|
+
type UntypedResolved<E> = NeverToAny<(E extends {
|
|
121
|
+
value: infer V;
|
|
122
|
+
} ? V : never) | (E extends {
|
|
123
|
+
default: infer D;
|
|
124
|
+
} ? D : never) | (E extends Record<string, any> ? E[Exclude<keyof E, ReservedEntryKeys>] : never)>;
|
|
112
125
|
type ResolveEntryType<E> = E extends {
|
|
113
126
|
format: StringConstructor;
|
|
114
|
-
} ? string : E extends {
|
|
127
|
+
} ? MaybeOptionalUndefined<E, string> : E extends {
|
|
115
128
|
format: NumberConstructor;
|
|
116
|
-
} ? number : E extends {
|
|
129
|
+
} ? MaybeOptionalUndefined<E, number> : E extends {
|
|
117
130
|
format: BooleanConstructor;
|
|
118
|
-
} ? boolean : E extends {
|
|
131
|
+
} ? MaybeOptionalUndefined<E, boolean> : E extends {
|
|
119
132
|
format: 'url';
|
|
120
|
-
} ? string : E extends {
|
|
133
|
+
} ? MaybeOptionalUndefined<E, string> : E extends {
|
|
121
134
|
format: (infer F)[];
|
|
122
|
-
} ? F : E extends {
|
|
135
|
+
} ? MaybeOptionalUndefined<E, F> : E extends {
|
|
123
136
|
format: ArrayConstructor;
|
|
124
|
-
} ? any[] :
|
|
137
|
+
} ? MaybeOptionalUndefined<E, any[]> : MaybeOptionalUndefined<E, UntypedResolved<E>>;
|
|
125
138
|
type ResolveConfigGroup<G> = { [K in keyof G]: G[K] extends {
|
|
126
139
|
doc: string;
|
|
127
140
|
} ? ResolveEntryType<G[K]> : ResolveConfigGroup<G[K]> };
|
|
@@ -158,6 +171,8 @@ type ValidateSchema<G, E extends EnvsShape> = { [K in keyof G]: G[K] extends {
|
|
|
158
171
|
format: readonly (infer V)[];
|
|
159
172
|
} ? { [P in keyof G[K]]: P extends EnvName<E> | "value" | "default" ? G[K][P] extends V | undefined ? G[K][P] : V : G[K][P] } : G[K] extends {
|
|
160
173
|
format: unknown;
|
|
174
|
+
} ? G[K] : G[K] extends {
|
|
175
|
+
doc: string;
|
|
161
176
|
} ? G[K] : ValidateSchema<G[K], E> };
|
|
162
177
|
//#endregion
|
|
163
178
|
//#region src/create-config.d.ts
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/util-types.ts","../src/types.ts","../src/create-config.ts","../src/define-config.ts"],"mappings":";;AAoBA;;;;AAA8B;AAAa;;;;;;;;;;;;;KAA/B,SAAA,GAAY,MAAM;AAAA,KAEzB,YAAA,oBAES,CAAA,gBAAiB,IAAA,CAAK,CAAA,EAAG,CAAA,YAAa,CAAA,SAC5C,CAAA;AAAA,KAGH,YAAA,oBAES,CAAA,gBAAiB,IAAA,CAAK,CAAA,EAAG,CAAA,IAAK,CAAA,iBACpC,CAAA;AANC;AAAA,KAUG,OAAA,WAAkB,SAAA,UAAmB,CAAC;;;;;KAMtC,MAAA,WAAiB,SAAA,eAAwB,YAAA,CAAa,CAAA,IAAK,CAAA,aAC/D,YAAA,CAAa,CAAA,KAAM,CAAA;;;;;;;;;;;AAXlB;AAIT;;;;;;;;AAAkD;AAMlD;;;;;;;KA+BY,SAAA,WAAoB,SAAA,IAAa,OAAA,CAC3C,MAAA,CAAO,OAAA,CAAQ,CAAA,GAAI,OAAA,CAAQ,CAAA;;;;;KAOjB,mBAAA,WAA8B,SAAA;EAvCb;;;;EA4C3B,SAAA,GAAY,SAAA,CAAU,CAAA;AAAA;;;KCjFnB,SAAA,WAAoB,SAAA,YAAqB,OAAA,CAAQ,CAAA;AAAA,KAGjD,qBAAA;EACC,KAAA,GAAQ,CAAA;EAAG,UAAA;EAAoB,aAAA;AAAA;EAC/B,KAAA,GAAQ,CAAA;EAAG,UAAA;EAAoB,aAAA;AAAA;EAC/B,KAAA,GAAQ,CAAA;EAAG,UAAA;EAAoB,aAAA;AAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/util-types.ts","../src/types.ts","../src/create-config.ts","../src/define-config.ts"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -2,28 +2,28 @@
|
|
|
2
2
|
function validateAndCoerce(value, format, fullKey, errors) {
|
|
3
3
|
switch (format) {
|
|
4
4
|
case String:
|
|
5
|
-
if (typeof value !== "string") errors.push(
|
|
5
|
+
if (typeof value !== "string") errors.push(`${fullKey}: must be a string`);
|
|
6
6
|
break;
|
|
7
7
|
case Number:
|
|
8
8
|
value = Number(value);
|
|
9
|
-
if (isNaN(value)) errors.push(
|
|
9
|
+
if (isNaN(value)) errors.push(`${fullKey}: must be a number`);
|
|
10
10
|
break;
|
|
11
11
|
case Boolean:
|
|
12
|
-
if (typeof value !== "boolean" && value !== "true" && value !== "false" && value !== 1 && value !== 0) errors.push(
|
|
12
|
+
if (typeof value !== "boolean" && value !== "true" && value !== "false" && value !== 1 && value !== 0) errors.push(`${fullKey}: must be a boolean`);
|
|
13
13
|
value = value === "true" ? true : value === "false" ? false : Boolean(value);
|
|
14
14
|
break;
|
|
15
15
|
case Array:
|
|
16
|
-
if (!Array.isArray(value)) errors.push(
|
|
16
|
+
if (!Array.isArray(value)) errors.push(`${fullKey}: must be an array`);
|
|
17
17
|
break;
|
|
18
18
|
case "url":
|
|
19
19
|
try {
|
|
20
20
|
new URL(value);
|
|
21
21
|
} catch {
|
|
22
|
-
errors.push(
|
|
22
|
+
errors.push(`${fullKey}: must be a valid URL; found "${value}"`);
|
|
23
23
|
}
|
|
24
24
|
break;
|
|
25
25
|
default: if (format instanceof Array) {
|
|
26
|
-
if (!format.includes(value)) errors.push(
|
|
26
|
+
if (!format.includes(value)) errors.push(`${fullKey}: must be one of: [${format.join(", ")}]`);
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
return value;
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/format.ts","../src/create-config.ts","../src/define-config.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\n\nexport function validateAndCoerce(\n value: any,\n format: any,\n fullKey: string,\n errors: string[],\n): any {\n switch (format) {\n case String:\n if (typeof value !== \"string\") {\n errors.push(`Config value for ${fullKey} must be a string`)\n }\n break\n case Number:\n value = Number(value)\n if (isNaN(value))\n errors.push(`Config value for ${fullKey} must be a number`)\n break\n case Boolean:\n if (\n typeof value !== \"boolean\" &&\n value !== \"true\" &&\n value !== \"false\" &&\n value !== 1 &&\n value !== 0\n ) {\n errors.push(`Config value for ${fullKey} must be a boolean`)\n }\n value =\n value === \"true\" ? true : value === \"false\" ? false : Boolean(value)\n break\n case Array:\n if (!Array.isArray(value)) {\n errors.push(`Config value for ${fullKey} must be an array`)\n }\n break\n case \"url\":\n try {\n new URL(value)\n } catch {\n errors.push(\n `Config value for ${fullKey} must be a valid URL; found \"${value}\"`,\n )\n }\n break\n default:\n if (format instanceof Array) {\n if (!format.includes(value)) {\n errors.push(\n `Config value for ${fullKey} must be one of: [${format.join(\", \")}]`,\n )\n }\n }\n }\n\n return value\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { validateAndCoerce } from \"./format.js\"\nimport type {\n ConfigGroup,\n ResolveConfigGroup,\n ValidateSchema,\n} from \"./types.js\"\nimport type {\n CreateConfigOptions,\n EnvName,\n EnvsShape,\n Fallbacks,\n} from \"./util-types.js\"\n\n/**\n * Create a resolved, validated config for the given environment.\n *\n * Curried so the envs declaration is bound on the first call and the\n * schema is inferred (giving you autocomplete) on the second call.\n *\n * @typeParam E - The envs shape describing required/optional environments.\n *\n * @example\n * ```ts\n * type MyEnvs = {\n * dev?: unknown\n * staging: unknown\n * production: unknown\n * }\n * const config = createEnvironmentConfig<MyEnvs>()('dev', {\n * port: { doc: 'Port', format: Number, value: 3000 },\n * })\n * config.port // number\n * ```\n *\n * @example Fallback environments\n * ```ts\n * // When running in `dev`, any entry that does not declare a `dev` value\n * // falls back to the entry's `integ` value.\n * const config = createEnvironmentConfig<MyEnvs>()(\n * 'dev',\n * {\n * apiUrl: {\n * doc: 'API URL',\n * format: 'url',\n * integ: 'https://integ.example.com',\n * staging: 'https://staging.example.com',\n * production: 'https://api.example.com',\n * },\n * },\n * { fallbacks: { dev: 'integ' } },\n * )\n * ```\n */\nexport function createEnvironmentConfig<E extends EnvsShape>() {\n return <const G extends ConfigGroup<E>>(\n env: EnvName<E>,\n inputConfig: ValidateSchema<G, E>,\n options?: CreateConfigOptions<E>,\n ): ResolveConfigGroup<G> & { env: EnvName<E> } =>\n buildConfig<E, G>(env, inputConfig as unknown as G, options)\n}\n\nfunction buildConfig<E extends EnvsShape, G extends ConfigGroup<E>>(\n env: EnvName<E>,\n inputConfig: G,\n options?: CreateConfigOptions<E>,\n): ResolveConfigGroup<G> & { env: EnvName<E> } {\n const errors: string[] = []\n\n // Resolve the per-environment lookup chain once for the active env.\n // Throws synchronously on a circular fallback chain.\n const envChain = resolveFallbackChain<E>(env, options?.fallbacks)\n\n function processConfig(\n config: ConfigGroup<E>,\n keyPrefix: string,\n ): Record<string, any> {\n const output: Record<string, any> = {}\n\n for (const [key, entry] of Object.entries(config)) {\n if (key === \"env\") {\n throw new Error(\n `Config key \"env\" is reserved and cannot be used. It will already be present by default.`,\n )\n }\n\n const fullKey = keyPrefix ? `${keyPrefix}.${key}` : key\n\n if (!(\"doc\" in entry)) {\n output[key] = processConfig(entry as ConfigGroup<E>, fullKey)\n continue\n }\n\n const configEntry = entry as any\n\n // Value resolution — sources are evaluated in ascending priority order.\n // The highest-priority source that resolves to a defined value wins.\n //\n // Priority │ Source\n // ─────────┼────────────────────────────────────────────────────────────────\n // 1 (low) │ Static `value` — same value across all environments\n // 2 │ Per-environment field, walking the fallback chain\n // │ — overrides the static value for that specific environment\n // 3 (high) │ Runtime env var via `processEnv` or `importMetaEnv`\n // │ — always wins; intended for secrets and local dev overrides\n\n // Priority 1: static value (lowest precedence)\n let value: any = \"value\" in configEntry ? configEntry.value : undefined\n\n // Priority 2: per-environment value, walking the fallback chain.\n // The first env in the chain with a defined value wins.\n for (const candidateEnv of envChain) {\n const envValue = configEntry[candidateEnv]\n if (envValue !== undefined) {\n value = envValue\n break\n }\n }\n\n // Priority 3: runtime env var (highest precedence — always wins when defined)\n if (\"processEnv\" in configEntry) {\n const runtimeOverride =\n // @ts-expect-error process may not be defined in browser builds\n typeof process !== \"undefined\" && process.env\n ? // @ts-expect-error process may not be defined in browser builds\n process.env[configEntry.processEnv as string]\n : undefined\n if (runtimeOverride !== undefined) value = runtimeOverride\n } else if (\"importMetaEnv\" in configEntry) {\n const runtimeOverride =\n // @ts-expect-error import.meta.env may not be defined in Node builds\n typeof import.meta !== \"undefined\" && import.meta.env\n ? // @ts-expect-error import.meta.env may not be defined in Node builds\n import.meta.env[configEntry.importMetaEnv as string]\n : undefined\n if (runtimeOverride !== undefined) value = runtimeOverride\n }\n\n const hasValueSource =\n value !== undefined ||\n \"processEnv\" in configEntry ||\n \"importMetaEnv\" in configEntry ||\n configEntry.optional\n\n if (value === undefined && !hasValueSource) {\n errors.push(\n `${fullKey}: No value source declared and \"optional\" is not set.`,\n )\n continue\n }\n\n if (value === undefined) {\n if (configEntry.optional) {\n value = configEntry.default\n if (value === undefined) {\n output[key] = undefined\n continue\n }\n } else {\n errors.push(\n `${fullKey}: Missing required config value in environment ${env}`,\n )\n continue\n }\n }\n\n //\n // Format validation and coercion\n //\n value = validateAndCoerce(value, configEntry.format, fullKey, errors)\n\n output[key] = value\n }\n\n return output\n }\n\n let outputConfig = processConfig(inputConfig, \"\")\n\n if (errors.length > 0) {\n console.error(\"Environment config validation failed\", errors)\n throw new Error(\n `Environment config validation failed:\\n${errors.join(\"\\n\")}`,\n )\n }\n\n outputConfig = {\n env,\n ...outputConfig,\n }\n return outputConfig as ResolveConfigGroup<G> & { env: EnvName<E> }\n}\n\n/**\n * Build the ordered list of environments to consult for per-environment\n * value resolution. The active env is always first; each subsequent entry\n * is the fallback target declared for the previous env. Throws if the\n * chain is cyclic.\n */\nfunction resolveFallbackChain<E extends EnvsShape>(\n env: EnvName<E>,\n fallbacks: Fallbacks<E> | undefined,\n): EnvName<E>[] {\n const chain: EnvName<E>[] = [env]\n if (!fallbacks) return chain\n\n const seen = new Set<string>([env])\n let current: EnvName<E> = env\n while (fallbacks[current] !== undefined) {\n const next = fallbacks[current] as EnvName<E>\n if (seen.has(next)) {\n throw new Error(\n `Circular fallback chain detected: ${[...chain, next].join(\" -> \")}`,\n )\n }\n seen.add(next)\n chain.push(next)\n current = next\n }\n return chain\n}\n","import { createEnvironmentConfig } from \"./create-config.js\"\nimport type {\n ConfigGroup,\n ResolveConfigGroup,\n ValidateSchema,\n} from \"./types.js\"\nimport type { CreateConfigOptions, EnvName, EnvsShape } from \"./util-types.js\"\n\n/**\n * Like {@link createEnvironmentConfig}, but binds the schema first and the\n * environment later. Useful when the active environment is not known at\n * schema-definition time.\n *\n * @typeParam E - The envs shape describing required/optional environments.\n *\n * @example\n * ```ts\n * type MyEnvs = {\n * dev?: unknown\n * staging: unknown\n * production: unknown\n * }\n * const buildConfig = defineEnvironmentConfig<MyEnvs>()({\n * port: { doc: 'Port', format: Number, value: 3000 },\n * })\n * const config = buildConfig('dev')\n * ```\n *\n * @example Fallback environments\n * ```ts\n * const buildConfig = defineEnvironmentConfig<MyEnvs>()(\n * { apiUrl: { doc: 'API URL', format: 'url', staging: 'https://staging' } },\n * { fallbacks: { dev: 'staging' } },\n * )\n * const config = buildConfig('dev') // apiUrl resolved from `staging`\n * ```\n */\nexport function defineEnvironmentConfig<E extends EnvsShape>() {\n const create = createEnvironmentConfig<E>()\n return <const G extends ConfigGroup<E>>(\n inputConfig: ValidateSchema<G, E>,\n options?: CreateConfigOptions<E>,\n ): ((env: EnvName<E>) => ResolveConfigGroup<G> & { env: EnvName<E> }) =>\n (env: EnvName<E>) =>\n create(env, inputConfig as any, options)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/format.ts","../src/create-config.ts","../src/define-config.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\n\nexport function validateAndCoerce(\n value: any,\n format: any,\n fullKey: string,\n errors: string[],\n): any {\n switch (format) {\n case String:\n if (typeof value !== \"string\") {\n errors.push(`${fullKey}: must be a string`)\n }\n break\n case Number:\n value = Number(value)\n if (isNaN(value))\n errors.push(`${fullKey}: must be a number`)\n break\n case Boolean:\n if (\n typeof value !== \"boolean\" &&\n value !== \"true\" &&\n value !== \"false\" &&\n value !== 1 &&\n value !== 0\n ) {\n errors.push(`${fullKey}: must be a boolean`)\n }\n value =\n value === \"true\" ? true : value === \"false\" ? false : Boolean(value)\n break\n case Array:\n if (!Array.isArray(value)) {\n errors.push(`${fullKey}: must be an array`)\n }\n break\n case \"url\":\n try {\n new URL(value)\n } catch {\n errors.push(\n `${fullKey}: must be a valid URL; found \"${value}\"`,\n )\n }\n break\n default:\n if (format instanceof Array) {\n if (!format.includes(value)) {\n errors.push(\n `${fullKey}: must be one of: [${format.join(\", \")}]`,\n )\n }\n }\n }\n\n return value\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { validateAndCoerce } from \"./format.js\"\nimport type {\n ConfigGroup,\n ResolveConfigGroup,\n ValidateSchema,\n} from \"./types.js\"\nimport type {\n CreateConfigOptions,\n EnvName,\n EnvsShape,\n Fallbacks,\n} from \"./util-types.js\"\n\n/**\n * Create a resolved, validated config for the given environment.\n *\n * Curried so the envs declaration is bound on the first call and the\n * schema is inferred (giving you autocomplete) on the second call.\n *\n * @typeParam E - The envs shape describing required/optional environments.\n *\n * @example\n * ```ts\n * type MyEnvs = {\n * dev?: unknown\n * staging: unknown\n * production: unknown\n * }\n * const config = createEnvironmentConfig<MyEnvs>()('dev', {\n * port: { doc: 'Port', format: Number, value: 3000 },\n * })\n * config.port // number\n * ```\n *\n * @example Fallback environments\n * ```ts\n * // When running in `dev`, any entry that does not declare a `dev` value\n * // falls back to the entry's `integ` value.\n * const config = createEnvironmentConfig<MyEnvs>()(\n * 'dev',\n * {\n * apiUrl: {\n * doc: 'API URL',\n * format: 'url',\n * integ: 'https://integ.example.com',\n * staging: 'https://staging.example.com',\n * production: 'https://api.example.com',\n * },\n * },\n * { fallbacks: { dev: 'integ' } },\n * )\n * ```\n */\nexport function createEnvironmentConfig<E extends EnvsShape>() {\n return <const G extends ConfigGroup<E>>(\n env: EnvName<E>,\n inputConfig: ValidateSchema<G, E>,\n options?: CreateConfigOptions<E>,\n ): ResolveConfigGroup<G> & { env: EnvName<E> } =>\n buildConfig<E, G>(env, inputConfig as unknown as G, options)\n}\n\nfunction buildConfig<E extends EnvsShape, G extends ConfigGroup<E>>(\n env: EnvName<E>,\n inputConfig: G,\n options?: CreateConfigOptions<E>,\n): ResolveConfigGroup<G> & { env: EnvName<E> } {\n const errors: string[] = []\n\n // Resolve the per-environment lookup chain once for the active env.\n // Throws synchronously on a circular fallback chain.\n const envChain = resolveFallbackChain<E>(env, options?.fallbacks)\n\n function processConfig(\n config: ConfigGroup<E>,\n keyPrefix: string,\n ): Record<string, any> {\n const output: Record<string, any> = {}\n\n for (const [key, entry] of Object.entries(config)) {\n if (key === \"env\") {\n throw new Error(\n `Config key \"env\" is reserved and cannot be used. It will already be present by default.`,\n )\n }\n\n const fullKey = keyPrefix ? `${keyPrefix}.${key}` : key\n\n if (!(\"doc\" in entry)) {\n output[key] = processConfig(entry as ConfigGroup<E>, fullKey)\n continue\n }\n\n const configEntry = entry as any\n\n // Value resolution — sources are evaluated in ascending priority order.\n // The highest-priority source that resolves to a defined value wins.\n //\n // Priority │ Source\n // ─────────┼────────────────────────────────────────────────────────────────\n // 1 (low) │ Static `value` — same value across all environments\n // 2 │ Per-environment field, walking the fallback chain\n // │ — overrides the static value for that specific environment\n // 3 (high) │ Runtime env var via `processEnv` or `importMetaEnv`\n // │ — always wins; intended for secrets and local dev overrides\n\n // Priority 1: static value (lowest precedence)\n let value: any = \"value\" in configEntry ? configEntry.value : undefined\n\n // Priority 2: per-environment value, walking the fallback chain.\n // The first env in the chain with a defined value wins.\n for (const candidateEnv of envChain) {\n const envValue = configEntry[candidateEnv]\n if (envValue !== undefined) {\n value = envValue\n break\n }\n }\n\n // Priority 3: runtime env var (highest precedence — always wins when defined)\n if (\"processEnv\" in configEntry) {\n const runtimeOverride =\n // @ts-expect-error process may not be defined in browser builds\n typeof process !== \"undefined\" && process.env\n ? // @ts-expect-error process may not be defined in browser builds\n process.env[configEntry.processEnv as string]\n : undefined\n if (runtimeOverride !== undefined) value = runtimeOverride\n } else if (\"importMetaEnv\" in configEntry) {\n const runtimeOverride =\n // @ts-expect-error import.meta.env may not be defined in Node builds\n typeof import.meta !== \"undefined\" && import.meta.env\n ? // @ts-expect-error import.meta.env may not be defined in Node builds\n import.meta.env[configEntry.importMetaEnv as string]\n : undefined\n if (runtimeOverride !== undefined) value = runtimeOverride\n }\n\n const hasValueSource =\n value !== undefined ||\n \"processEnv\" in configEntry ||\n \"importMetaEnv\" in configEntry ||\n configEntry.optional\n\n if (value === undefined && !hasValueSource) {\n errors.push(\n `${fullKey}: No value source declared and \"optional\" is not set.`,\n )\n continue\n }\n\n if (value === undefined) {\n if (configEntry.optional) {\n value = configEntry.default\n if (value === undefined) {\n output[key] = undefined\n continue\n }\n } else {\n errors.push(\n `${fullKey}: Missing required config value in environment ${env}`,\n )\n continue\n }\n }\n\n //\n // Format validation and coercion\n //\n value = validateAndCoerce(value, configEntry.format, fullKey, errors)\n\n output[key] = value\n }\n\n return output\n }\n\n let outputConfig = processConfig(inputConfig, \"\")\n\n if (errors.length > 0) {\n console.error(\"Environment config validation failed\", errors)\n throw new Error(\n `Environment config validation failed:\\n${errors.join(\"\\n\")}`,\n )\n }\n\n outputConfig = {\n env,\n ...outputConfig,\n }\n return outputConfig as ResolveConfigGroup<G> & { env: EnvName<E> }\n}\n\n/**\n * Build the ordered list of environments to consult for per-environment\n * value resolution. The active env is always first; each subsequent entry\n * is the fallback target declared for the previous env. Throws if the\n * chain is cyclic.\n */\nfunction resolveFallbackChain<E extends EnvsShape>(\n env: EnvName<E>,\n fallbacks: Fallbacks<E> | undefined,\n): EnvName<E>[] {\n const chain: EnvName<E>[] = [env]\n if (!fallbacks) return chain\n\n const seen = new Set<string>([env])\n let current: EnvName<E> = env\n while (fallbacks[current] !== undefined) {\n const next = fallbacks[current] as EnvName<E>\n if (seen.has(next)) {\n throw new Error(\n `Circular fallback chain detected: ${[...chain, next].join(\" -> \")}`,\n )\n }\n seen.add(next)\n chain.push(next)\n current = next\n }\n return chain\n}\n","import { createEnvironmentConfig } from \"./create-config.js\"\nimport type {\n ConfigGroup,\n ResolveConfigGroup,\n ValidateSchema,\n} from \"./types.js\"\nimport type { CreateConfigOptions, EnvName, EnvsShape } from \"./util-types.js\"\n\n/**\n * Like {@link createEnvironmentConfig}, but binds the schema first and the\n * environment later. Useful when the active environment is not known at\n * schema-definition time.\n *\n * @typeParam E - The envs shape describing required/optional environments.\n *\n * @example\n * ```ts\n * type MyEnvs = {\n * dev?: unknown\n * staging: unknown\n * production: unknown\n * }\n * const buildConfig = defineEnvironmentConfig<MyEnvs>()({\n * port: { doc: 'Port', format: Number, value: 3000 },\n * })\n * const config = buildConfig('dev')\n * ```\n *\n * @example Fallback environments\n * ```ts\n * const buildConfig = defineEnvironmentConfig<MyEnvs>()(\n * { apiUrl: { doc: 'API URL', format: 'url', staging: 'https://staging' } },\n * { fallbacks: { dev: 'staging' } },\n * )\n * const config = buildConfig('dev') // apiUrl resolved from `staging`\n * ```\n */\nexport function defineEnvironmentConfig<E extends EnvsShape>() {\n const create = createEnvironmentConfig<E>()\n return <const G extends ConfigGroup<E>>(\n inputConfig: ValidateSchema<G, E>,\n options?: CreateConfigOptions<E>,\n ): ((env: EnvName<E>) => ResolveConfigGroup<G> & { env: EnvName<E> }) =>\n (env: EnvName<E>) =>\n create(env, inputConfig as any, options)\n}\n"],"mappings":"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"}
|