@maverickcer/env-cap 0.2.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/LICENSE +21 -0
- package/README.md +279 -0
- package/dist/.dts/build/assemble-project.d.ts +47 -0
- package/dist/.dts/build/assemble-project.d.ts.map +1 -0
- package/dist/.dts/build/change-model.d.ts +62 -0
- package/dist/.dts/build/change-model.d.ts.map +1 -0
- package/dist/.dts/build/check-artifacts.d.ts +48 -0
- package/dist/.dts/build/check-artifacts.d.ts.map +1 -0
- package/dist/.dts/build/citation-verification.d.ts +62 -0
- package/dist/.dts/build/citation-verification.d.ts.map +1 -0
- package/dist/.dts/build/compatibility.d.ts +76 -0
- package/dist/.dts/build/compatibility.d.ts.map +1 -0
- package/dist/.dts/build/contract-model.d.ts +92 -0
- package/dist/.dts/build/contract-model.d.ts.map +1 -0
- package/dist/.dts/build/deep-equal.d.ts +9 -0
- package/dist/.dts/build/deep-equal.d.ts.map +1 -0
- package/dist/.dts/build/deep-freeze.d.ts +20 -0
- package/dist/.dts/build/deep-freeze.d.ts.map +1 -0
- package/dist/.dts/build/dependency-graph.d.ts +168 -0
- package/dist/.dts/build/dependency-graph.d.ts.map +1 -0
- package/dist/.dts/build/dependency-model.d.ts +79 -0
- package/dist/.dts/build/dependency-model.d.ts.map +1 -0
- package/dist/.dts/build/discover.d.ts +25 -0
- package/dist/.dts/build/discover.d.ts.map +1 -0
- package/dist/.dts/build/display-path.d.ts +24 -0
- package/dist/.dts/build/display-path.d.ts.map +1 -0
- package/dist/.dts/build/docs.d.ts +176 -0
- package/dist/.dts/build/docs.d.ts.map +1 -0
- package/dist/.dts/build/env-example.d.ts +85 -0
- package/dist/.dts/build/env-example.d.ts.map +1 -0
- package/dist/.dts/build/errors.d.ts +78 -0
- package/dist/.dts/build/errors.d.ts.map +1 -0
- package/dist/.dts/build/evidence-cache.d.ts +74 -0
- package/dist/.dts/build/evidence-cache.d.ts.map +1 -0
- package/dist/.dts/build/evidence-model.d.ts +50 -0
- package/dist/.dts/build/evidence-model.d.ts.map +1 -0
- package/dist/.dts/build/evidence-reference.d.ts +67 -0
- package/dist/.dts/build/evidence-reference.d.ts.map +1 -0
- package/dist/.dts/build/evidence-snapshot.d.ts +123 -0
- package/dist/.dts/build/evidence-snapshot.d.ts.map +1 -0
- package/dist/.dts/build/exclusive-group.d.ts +19 -0
- package/dist/.dts/build/exclusive-group.d.ts.map +1 -0
- package/dist/.dts/build/finding-model.d.ts +79 -0
- package/dist/.dts/build/finding-model.d.ts.map +1 -0
- package/dist/.dts/build/generate-documentation.d.ts +176 -0
- package/dist/.dts/build/generate-documentation.d.ts.map +1 -0
- package/dist/.dts/build/generate-env-artifacts.d.ts +176 -0
- package/dist/.dts/build/generate-env-artifacts.d.ts.map +1 -0
- package/dist/.dts/build/generate-evidence.d.ts +77 -0
- package/dist/.dts/build/generate-evidence.d.ts.map +1 -0
- package/dist/.dts/build/generate-manifest.d.ts +101 -0
- package/dist/.dts/build/generate-manifest.d.ts.map +1 -0
- package/dist/.dts/build/generate-usage.d.ts +95 -0
- package/dist/.dts/build/generate-usage.d.ts.map +1 -0
- package/dist/.dts/build/generated-banner.d.ts +52 -0
- package/dist/.dts/build/generated-banner.d.ts.map +1 -0
- package/dist/.dts/build/glob.d.ts +18 -0
- package/dist/.dts/build/glob.d.ts.map +1 -0
- package/dist/.dts/build/governance-fields.d.ts +34 -0
- package/dist/.dts/build/governance-fields.d.ts.map +1 -0
- package/dist/.dts/build/humanize-key.d.ts +14 -0
- package/dist/.dts/build/humanize-key.d.ts.map +1 -0
- package/dist/.dts/build/index.d.ts +107 -0
- package/dist/.dts/build/index.d.ts.map +1 -0
- package/dist/.dts/build/lifecycle-model.d.ts +64 -0
- package/dist/.dts/build/lifecycle-model.d.ts.map +1 -0
- package/dist/.dts/build/link.d.ts +215 -0
- package/dist/.dts/build/link.d.ts.map +1 -0
- package/dist/.dts/build/literal-eval.d.ts +23 -0
- package/dist/.dts/build/literal-eval.d.ts.map +1 -0
- package/dist/.dts/build/live-expirations.d.ts +47 -0
- package/dist/.dts/build/live-expirations.d.ts.map +1 -0
- package/dist/.dts/build/manifest-snapshot.d.ts +229 -0
- package/dist/.dts/build/manifest-snapshot.d.ts.map +1 -0
- package/dist/.dts/build/manifest.d.ts +31 -0
- package/dist/.dts/build/manifest.d.ts.map +1 -0
- package/dist/.dts/build/map-utils.d.ts +8 -0
- package/dist/.dts/build/map-utils.d.ts.map +1 -0
- package/dist/.dts/build/ownership-model.d.ts +57 -0
- package/dist/.dts/build/ownership-model.d.ts.map +1 -0
- package/dist/.dts/build/parse.d.ts +199 -0
- package/dist/.dts/build/parse.d.ts.map +1 -0
- package/dist/.dts/build/reference-projections.d.ts +156 -0
- package/dist/.dts/build/reference-projections.d.ts.map +1 -0
- package/dist/.dts/build/resolution/resolve-import.d.ts +53 -0
- package/dist/.dts/build/resolution/resolve-import.d.ts.map +1 -0
- package/dist/.dts/build/resolution/resolve-package-schema.d.ts +83 -0
- package/dist/.dts/build/resolution/resolve-package-schema.d.ts.map +1 -0
- package/dist/.dts/build/resolution/resolve-tsconfig-paths.d.ts +101 -0
- package/dist/.dts/build/resolution/resolve-tsconfig-paths.d.ts.map +1 -0
- package/dist/.dts/build/resolution/resolve-within-root.d.ts +35 -0
- package/dist/.dts/build/resolution/resolve-within-root.d.ts.map +1 -0
- package/dist/.dts/build/resolve-import.d.ts +50 -0
- package/dist/.dts/build/resolve-import.d.ts.map +1 -0
- package/dist/.dts/build/resolve-package-schema.d.ts +99 -0
- package/dist/.dts/build/resolve-package-schema.d.ts.map +1 -0
- package/dist/.dts/build/resolve-tsconfig-paths.d.ts +100 -0
- package/dist/.dts/build/resolve-tsconfig-paths.d.ts.map +1 -0
- package/dist/.dts/build/resolve-within-root.d.ts +35 -0
- package/dist/.dts/build/resolve-within-root.d.ts.map +1 -0
- package/dist/.dts/build/sarif.d.ts +66 -0
- package/dist/.dts/build/sarif.d.ts.map +1 -0
- package/dist/.dts/build/scan-dependencies.d.ts +47 -0
- package/dist/.dts/build/scan-dependencies.d.ts.map +1 -0
- package/dist/.dts/build/sort-by-identity.d.ts +14 -0
- package/dist/.dts/build/sort-by-identity.d.ts.map +1 -0
- package/dist/.dts/build/source-position.d.ts +69 -0
- package/dist/.dts/build/source-position.d.ts.map +1 -0
- package/dist/.dts/build/tool-version.d.ts +8 -0
- package/dist/.dts/build/tool-version.d.ts.map +1 -0
- package/dist/.dts/build/types.d.ts +53 -0
- package/dist/.dts/build/types.d.ts.map +1 -0
- package/dist/.dts/build/usage-report.d.ts +124 -0
- package/dist/.dts/build/usage-report.d.ts.map +1 -0
- package/dist/.dts/cli/filesystem.d.ts +3 -0
- package/dist/.dts/cli/filesystem.d.ts.map +1 -0
- package/dist/.dts/eslint-plugin/glob.d.ts +18 -0
- package/dist/.dts/eslint-plugin/glob.d.ts.map +1 -0
- package/dist/.dts/eslint-plugin/index.d.ts +27 -0
- package/dist/.dts/eslint-plugin/index.d.ts.map +1 -0
- package/dist/.dts/eslint-plugin/no-node-fs.d.ts +25 -0
- package/dist/.dts/eslint-plugin/no-node-fs.d.ts.map +1 -0
- package/dist/.dts/eslint-plugin/no-raw-process-env.d.ts +23 -0
- package/dist/.dts/eslint-plugin/no-raw-process-env.d.ts.map +1 -0
- package/dist/.dts/evidence/define-projection.d.ts +51 -0
- package/dist/.dts/evidence/define-projection.d.ts.map +1 -0
- package/dist/.dts/evidence/index.d.ts +23 -0
- package/dist/.dts/evidence/index.d.ts.map +1 -0
- package/dist/.dts/helpers/index.d.ts +135 -0
- package/dist/.dts/helpers/index.d.ts.map +1 -0
- package/dist/.dts/helpers/processors.d.ts +32 -0
- package/dist/.dts/helpers/processors.d.ts.map +1 -0
- package/dist/.dts/helpers/validators.d.ts +68 -0
- package/dist/.dts/helpers/validators.d.ts.map +1 -0
- package/dist/.dts/node/index.d.ts +2 -0
- package/dist/.dts/node/index.d.ts.map +1 -0
- package/dist/.dts/runtime/cache.d.ts +20 -0
- package/dist/.dts/runtime/cache.d.ts.map +1 -0
- package/dist/.dts/runtime/create.d.ts +20 -0
- package/dist/.dts/runtime/create.d.ts.map +1 -0
- package/dist/.dts/runtime/document.d.ts +181 -0
- package/dist/.dts/runtime/document.d.ts.map +1 -0
- package/dist/.dts/runtime/errors.d.ts +55 -0
- package/dist/.dts/runtime/errors.d.ts.map +1 -0
- package/dist/.dts/runtime/index.d.ts +24 -0
- package/dist/.dts/runtime/index.d.ts.map +1 -0
- package/dist/.dts/runtime/registry.d.ts +12 -0
- package/dist/.dts/runtime/registry.d.ts.map +1 -0
- package/dist/.dts/runtime/reset.d.ts +8 -0
- package/dist/.dts/runtime/reset.d.ts.map +1 -0
- package/dist/.dts/runtime/types.d.ts +175 -0
- package/dist/.dts/runtime/types.d.ts.map +1 -0
- package/dist/.dts/runtime/validate.d.ts +14 -0
- package/dist/.dts/runtime/validate.d.ts.map +1 -0
- package/dist/build.cjs +83 -0
- package/dist/build.cjs.map +1 -0
- package/dist/build.d.cts +1 -0
- package/dist/build.d.ts +1 -0
- package/dist/build.js +19 -0
- package/dist/build.js.map +1 -0
- package/dist/cli/index.js +134 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/eslint-plugin/index.cjs +12 -0
- package/dist/eslint-plugin/index.cjs.map +1 -0
- package/dist/eslint-plugin/index.d.cts +1 -0
- package/dist/eslint-plugin/index.d.ts +1 -0
- package/dist/eslint-plugin/index.js +5 -0
- package/dist/eslint-plugin/index.js.map +1 -0
- package/dist/evidence.cjs +7 -0
- package/dist/evidence.cjs.map +1 -0
- package/dist/evidence.d.cts +1 -0
- package/dist/evidence.d.ts +1 -0
- package/dist/evidence.js +5 -0
- package/dist/evidence.js.map +1 -0
- package/dist/helpers.cjs +8 -0
- package/dist/helpers.cjs.map +1 -0
- package/dist/helpers.d.cts +1 -0
- package/dist/helpers.d.ts +1 -0
- package/dist/helpers.js +5 -0
- package/dist/helpers.js.map +1 -0
- package/dist/index.cjs +15 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/node.cjs +13 -0
- package/dist/node.cjs.map +1 -0
- package/dist/node.d.cts +1 -0
- package/dist/node.d.ts +1 -0
- package/dist/node.js +7 -0
- package/dist/node.js.map +1 -0
- package/package.json +183 -0
- package/schemas/contract-model.schema.json +321 -0
- package/schemas/env-cap-report.schema.json +2369 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/helpers/processors.ts","../src/helpers/validators.ts","../src/helpers/index.ts"],"names":["processors","validators","length"],"mappings":"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","file":"helpers.cjs","sourcesContent":["import type { Processor } from \"../runtime/index.js\"\n\n/**\n * Convenience processors. Purely optional sugar over `Processor<T>` --\n * the runtime and generator never import from this module. Error messages\n * describe the *type* of an invalid value, never its content, so a\n * misconfigured secret is never echoed back through a thrown error.\n */\n\nfunction describe(value: unknown): string {\n if (value === undefined) return \"undefined\"\n if (value === null) return \"null\"\n return `a ${typeof value}`\n}\n\n/** Splits a delimited string (or passes through an array) into items, running each through `processors` in order. */\nexport function toArray<T>(\n separator: string | RegExp = \",\",\n processors: Processor<unknown>[] = [],\n): Processor<T[]> {\n return (value) => {\n const items = Array.isArray(value)\n ? value\n : typeof value === \"string\" && value.trim() !== \"\"\n ? value.split(separator).map((item) => item.trim())\n : []\n\n return items.map((item) => {\n return processors.reduce((current, processor) => processor(current), item) as T\n })\n }\n}\n\n/** Decodes a base64 string into a `Buffer`. */\nexport function base64(): Processor<Buffer> {\n return (value) => {\n try {\n return Buffer.from(String(value), \"base64\")\n } catch {\n throw new Error(\"Expected a valid base64 value.\")\n }\n }\n}\n\n/** Coerces a value to a `bigint`. */\nexport function toBigInt(): Processor<bigint> {\n return (value) => {\n try {\n return BigInt(String(value))\n } catch {\n throw new Error(\"Expected a bigint value.\")\n }\n }\n}\n\n/** Coerces common boolean-like strings (`true`/`1`/`yes`/`on`, and their opposites) into a real boolean. */\nexport function toBoolean(): Processor<boolean> {\n return (value) => {\n // Bypassing this fast-path is behaviorally equivalent, not a real gap:\n // `String(true)`/`String(false)` round-trip through the exact\n // \"true\"/\"false\" strings already recognized below, so falling through\n // to the general string-parsing path gives the identical result for a\n // real boolean input either way. Hand-verified: mutating this and\n // running the real suite passes unchanged.\n // Stryker disable next-line ConditionalExpression,StringLiteral\n if (typeof value === \"boolean\") return value\n const normalized = String(value).trim().toLowerCase()\n if ([\"true\", \"1\", \"yes\", \"on\"].includes(normalized)) return true\n if ([\"false\", \"0\", \"no\", \"off\"].includes(normalized)) return false\n throw new Error(\n `Expected a boolean-like value (true/false/1/0/yes/no/on/off), received ${describe(value)}.`,\n )\n }\n}\n\n/** Parses a value into a `Date`. */\nexport function toDate(): Processor<Date> {\n return (value) => {\n const result = new Date(String(value))\n\n if (Number.isNaN(result.getTime())) {\n throw new Error(\"Expected a valid date.\")\n }\n\n return result\n }\n}\n\n/** Coerces a value to a number and requires it to be an integer. */\nexport function toInteger(): Processor<number> {\n return (value) => {\n const parsed = toNumber()(value)\n\n if (!Number.isInteger(parsed)) {\n throw new Error(\"Expected an integer value.\")\n }\n\n return parsed\n }\n}\n\n/** Parses a JSON string; passes non-string values through unchanged. */\nexport function parseJSON<T = unknown>(): Processor<T> {\n return (value) => {\n if (typeof value !== \"string\") {\n if (value === undefined) {\n throw new Error(\"Expected a JSON string, received undefined.\")\n }\n return value as T\n }\n try {\n return JSON.parse(value) as T\n } catch {\n throw new Error(\"Expected a valid JSON string, but parsing failed.\")\n }\n }\n}\n\n/** Lowercases a string (coercing nullish values to `\"\"` first). */\nexport function toLowerCase(): Processor<string> {\n return (value) => String(value ?? \"\").toLowerCase()\n}\n\n/** Coerces a value to a number, rejecting empty/whitespace-only strings instead of silently resolving to `0`. */\nexport function toNumber(): Processor<number> {\n return (value) => {\n // `Number(\"\")` is `0`, not `NaN` -- without this check a blank env var would\n // silently resolve to the number 0 instead of failing, slipping past\n // validators.required() too (that checks the processed value, not the raw string).\n if (typeof value === \"string\" && value.trim() === \"\") {\n throw new Error(\"Expected a numeric value, received an empty string.\")\n }\n // Bypassing this type-check (always going through `Number(value)`) is\n // behaviorally equivalent, not a real gap: `Number()` is idempotent on\n // an already-number input (`Number(x) === x` for every real number,\n // including NaN and -0), so the ternary's two branches always produce\n // the same result. Hand-verified: mutating this and running the real\n // suite passes unchanged.\n // Stryker disable next-line ConditionalExpression,StringLiteral\n const result = typeof value === \"number\" ? value : Number(value)\n if (Number.isNaN(result)) {\n throw new Error(`Expected a numeric value, received ${describe(value)}.`)\n }\n return result\n }\n}\n\n/** Compiles a value into a `RegExp`. */\nexport function toRegExp(): Processor<RegExp> {\n return (value) => {\n try {\n return new RegExp(String(value))\n } catch {\n throw new Error(\"Expected a valid regular expression.\")\n }\n }\n}\n\n/** Splits a string on `separator` into a trimmed string array; returns `[]` for non-string input. */\nexport function split(separator = \",\"): Processor<string[]> {\n return (value) => {\n if (typeof value !== \"string\") return []\n\n return value.split(separator).map((item) => item.trim())\n }\n}\n\n/** Coerces a value to a string, treating nullish values as `\"\"`. */\nexport function toString(): Processor<string> {\n return (value) => {\n if (value === undefined || value === null) return \"\"\n // Bypassing this type-check (always going through `String(value)`) is\n // behaviorally equivalent, not a real gap: `String()` is idempotent on\n // an already-string input. Hand-verified: mutating this and running the\n // real suite passes unchanged.\n // Stryker disable next-line ConditionalExpression,StringLiteral\n return typeof value === \"string\" ? value : String(value)\n }\n}\n\n/** Trims surrounding whitespace from a string (coercing nullish values to `\"\"` first). */\nexport function trim(): Processor<string> {\n return (value) => String(value ?? \"\").trim()\n}\n\n/** Uppercases a string (coercing nullish values to `\"\"` first). */\nexport function toUpperCase(): Processor<string> {\n return (value) => String(value ?? \"\").toUpperCase()\n}\n\n/** Parses a value into a `URL`, treating nullish values as `\"\"` first. */\nexport function toURL(): Processor<URL> {\n return (value) => {\n try {\n // The `\"\"` fallback for nullish input is behaviorally equivalent to\n // any other non-URL-shaped fallback string: `new URL(...)` throws for\n // BOTH \"\" and any garbage string, and both land in the same generic\n // catch below with the same message -- there is no distinguishable\n // outcome. Hand-verified: mutating this and running the real suite\n // passes unchanged.\n // Stryker disable next-line StringLiteral\n return new URL(String(value ?? \"\"))\n } catch {\n throw new Error(\"Expected a valid absolute URL.\")\n }\n }\n}\n","import type { Validator } from \"../runtime/index.js\"\n\n/** Passes when the date value is strictly after `date`. */\nexport function after(date: Date): Validator<Date> {\n return (value, _rawEnv) => value > date || \"Expected date to be after required date.\"\n}\n\n/** Passes only when every wrapped validator passes; returns the first failing message. */\nexport function all<T>(...validators: Validator<T>[]): Validator<T> {\n return (value, rawEnv) => {\n for (const validator of validators) {\n const result = validator(value, rawEnv)\n if (result !== true) return result\n }\n\n return true\n }\n}\n\n/** Passes when at least one wrapped validator passes. */\nexport function any<T>(...validators: Validator<T>[]): Validator<T> {\n return (value, rawEnv) => {\n for (const validator of validators) {\n if (validator(value, rawEnv) === true) return true\n }\n\n return \"Value did not satisfy any validation.\"\n }\n}\n\n/** Passes when the date value is strictly before `date`. */\nexport function before(date: Date): Validator<Date> {\n return (value, _rawEnv) => value < date || \"Expected date to be before required date.\"\n}\n\n/** Identity wrapper for a hand-written `Validator<T>` -- no behavior change, just a fluent entry point alongside the other helpers. */\nexport function custom<T>(validator: Validator<T>): Validator<T> {\n return validator\n}\n\n/** Passes for a plausibly-shaped email address (`local@domain`). */\nexport function email(): Validator<string> {\n return matches(/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/, \"Expected a valid email address.\")\n}\n\n/** Passes when the string ends with `suffix`. */\nexport function endsWith(suffix: string): Validator<string> {\n return (value, _rawEnv) => value.endsWith(suffix) || \"Expected value to end with required suffix.\"\n}\n\n/** Passes when the number is finite (rejects `Infinity`/`-Infinity`/`NaN`). */\nexport function finite(): Validator<number> {\n return (value, _rawEnv) => Number.isFinite(value) || \"Expected a finite number.\"\n}\n\n/** Passes when the date is strictly after the current time. */\nexport function future(): Validator<Date> {\n return (value, _rawEnv) => value > new Date() || \"Expected a future date.\"\n}\n\n/** Passes when the string contains `text` as a substring. */\nexport function includes(text: string): Validator<string> {\n return (value, _rawEnv) => value.includes(text) || \"Expected value to include required text.\"\n}\n\n/** Passes when the number is an integer. */\nexport function integer(): Validator<number> {\n return (value, _rawEnv) => Number.isInteger(value) || \"Expected an integer value.\"\n}\n\n/** Passes when the string's length is exactly `expected`. */\nexport function length(expected: number): Validator<string> {\n return (value, _rawEnv) => value.length === expected || `Expected length ${expected}.`\n}\n\n/** Passes when the string matches `pattern`; `message` overrides the default failure text. */\nexport function matches(pattern: RegExp, message?: string): Validator<string> {\n return (value, _rawEnv) =>\n pattern.test(value) || (message ?? \"Expected value to match required pattern.\")\n}\n\n/** Passes when the number is less than or equal to `maximum`. */\nexport function max(maximum: number): Validator<number> {\n return (value, _rawEnv) =>\n value <= maximum || `Expected a value less than or equal to ${maximum}.`\n}\n\n/** Passes when the array has at most `maximum` items. */\nexport function maxItems<T>(maximum: number): Validator<T[]> {\n return (value, _rawEnv) => value.length <= maximum || `Expected no more than ${maximum} items.`\n}\n\n/** Passes when the string's length is at most `length`. */\nexport function maxLength(length: number): Validator<string> {\n return (value, _rawEnv) => value.length <= length || `Expected a maximum length of ${length}.`\n}\n\n/** Passes when the number is greater than or equal to `minimum`. */\nexport function min(minimum: number): Validator<number> {\n return (value, _rawEnv) =>\n value >= minimum || `Expected a value greater than or equal to ${minimum}.`\n}\n\n/** Passes when the array has at least `minimum` items. */\nexport function minItems<T>(minimum: number): Validator<T[]> {\n return (value, _rawEnv) => value.length >= minimum || `Expected at least ${minimum} items.`\n}\n\n/** Passes when the string's length is at least `length`. */\nexport function minLength(length: number): Validator<string> {\n return (value, _rawEnv) => value.length >= length || `Expected a minimum length of ${length}.`\n}\n\n/** Passes when the number is strictly negative. */\nexport function negative(): Validator<number> {\n return (value, _rawEnv) => value < 0 || \"Expected a negative value.\"\n}\n\n/** Inverts a validator: passes when the wrapped validator fails, and vice versa. */\nexport function not<T>(validator: Validator<T>): Validator<T> {\n return (value, rawEnv) =>\n validator(value, rawEnv) === true ? \"Value must not satisfy this validation.\" : true\n}\n\n/** Passes when the value is one of the `allowed` values. */\nexport function oneOf<T>(allowed: readonly T[]): Validator<T> {\n return (value, _rawEnv) => allowed.includes(value) || `Expected one of: ${allowed.join(\", \")}.`\n}\n\n/** Passes when the value is undefined, otherwise delegates to the provided validator. */\nexport function optional<T>(validator: Validator<T>): Validator<T | undefined> {\n return (value, rawEnv) => {\n if (value === undefined) return true\n return validator(value, rawEnv)\n }\n}\n\n/** Passes when the date is strictly before the current time. */\nexport function past(): Validator<Date> {\n return (value, _rawEnv) => value < new Date() || \"Expected a past date.\"\n}\n\n/** Passes when the number is strictly positive. */\nexport function positive(): Validator<number> {\n return (value, _rawEnv) => value > 0 || \"Expected a positive value.\"\n}\n\n/** Passes when the number is within `[minimum, maximum]` inclusive. */\nexport function range(minimum: number, maximum: number): Validator<number> {\n return (value, _rawEnv) =>\n (value >= minimum && value <= maximum) ||\n `Expected a value greater than or equal to ${minimum} and less than or equal to ${maximum}.`\n}\n\n/** Runs the wrapped validator but replaces its failure message with `message`. */\nexport function refine<T>(validator: Validator<T>, message: string): Validator<T> {\n return (value, rawEnv) => (validator(value, rawEnv) === true ? true : message)\n}\n\n/** Passes when the value is defined and, unless `allowEmptyString` is set, non-empty. */\nexport function required(allowEmptyString = false): Validator<unknown> {\n return (value, _rawEnv) => {\n if (value === undefined || value === null) {\n return \"This variable is required.\"\n }\n\n if (!allowEmptyString && typeof value === \"string\" && value.length === 0) {\n return \"This variable is required.\"\n }\n\n return true\n }\n}\n\n/** Passes when the number is a safe integer (`Number.isSafeInteger`). */\nexport function safeInteger(): Validator<number> {\n return (value, _rawEnv) => Number.isSafeInteger(value) || \"Expected a safe integer.\"\n}\n\n/** Passes when every item in the array is unique. */\nexport function unique<T>(): Validator<T[]> {\n return (value, _rawEnv) =>\n new Set(value).size === value.length || \"Expected all items to be unique.\"\n}\n\n/** Passes when the string is a valid absolute URL. */\nexport function url(): Validator<string> {\n return (value, _rawEnv) => {\n try {\n new URL(value)\n return true\n } catch {\n return \"Expected a valid absolute URL.\"\n }\n }\n}\n\n/** Passes when the string is a valid UUID of one of the given `versions` (default: any of 1-8). */\nexport function uuid(versions: readonly number[] = [1, 2, 3, 4, 5, 6, 7, 8]): Validator<string> {\n return (value, _rawEnv) => {\n const match =\n /^([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f])[0-9a-f]{3}-([89ab])[0-9a-f]{3}-([0-9a-f]{12})$/i.exec(\n value,\n )\n\n if (!match) {\n return \"Expected a valid UUID .\"\n }\n\n const version = Number.parseInt(match[3], 16)\n\n if (!versions.includes(version)) {\n return `Expected UUID version ${versions.join(\", \")}.`\n }\n\n return true\n }\n}\n\n/** Shorthand for `uuid([version])` -- passes only for that exact UUID version. */\nexport function uuidVersion(number: number): Validator<string> {\n return uuid([number])\n}\n","/**\n * Optional convenience helpers for common processor/validator patterns.\n * Entirely separate from the core runtime -- `defineEnv`/`validateEnv` have\n * no knowledge of this module and work identically without it.\n *\n * Usage: `processors.toNumber()`, `validators.range(1, 65535)`.\n *\n * Deliberately built from explicit named imports assembled into plain object\n * literals, not `export * as processors from \"./processors.js\"`. A bundler\n * (tsup/esbuild) lowers that `export * as` form into a namespace-construction\n * helper call it can't prove side-effect-free, which defeats tree-shaking in\n * a *second*, downstream bundling pass (a consumer's own bundler bundling\n * this already-bundled package): importing only `processors` would still\n * ship all of `validators`' code. A plain object literal of already-imported\n * bindings has no such call in the way, so `import { processors } from\n * \"env-cap/helpers\"` alone drops `validators` entirely.\n */\nimport {\n base64,\n parseJSON,\n split,\n toArray,\n toBigInt,\n toBoolean,\n toDate,\n toInteger,\n toLowerCase,\n toNumber,\n toRegExp,\n toString,\n toURL,\n toUpperCase,\n trim,\n} from \"./processors.js\"\nimport {\n after,\n all,\n any,\n before,\n custom,\n email,\n endsWith,\n finite,\n future,\n includes,\n integer as integerValidator,\n length,\n matches,\n max,\n maxItems,\n maxLength,\n min,\n minItems,\n minLength,\n negative,\n not,\n oneOf,\n optional,\n past,\n positive,\n range,\n refine,\n required,\n safeInteger,\n unique,\n url as urlValidator,\n uuid,\n uuidVersion,\n} from \"./validators.js\"\n\n/**\n * Convenience {@link runtime.Processor} implementations for common coercion\n * patterns (e.g. `processors.toNumber()`).\n *\n * @remarks\n * Naming matches `@maverickcer/data-cap`'s `helpers.processors` -- but these\n * throw with a formatted `Error` message on invalid input, where data-cap's\n * silently return `undefined`. Do not assume the same failure mode when\n * moving between packages.\n */\nexport const processors = {\n /** Decodes a base64 string into a `Buffer`. */\n base64,\n /** Parses a JSON string; passes non-string values through unchanged. */\n parseJSON,\n /** Splits a string on `separator` into a trimmed string array; returns `[]` for non-string input. */\n split,\n /** Splits a delimited string (or passes through an array) into items, running each through `processors` in order. */\n toArray,\n /** Coerces a value to a `bigint`. */\n toBigInt,\n /** Coerces common boolean-like strings (`true`/`1`/`yes`/`on`, and their opposites) into a real boolean. */\n toBoolean,\n /** Parses a value into a `Date`. */\n toDate,\n /** Coerces a value to a number and requires it to be an integer. */\n toInteger,\n /** Lowercases a string (coercing nullish values to `\"\"` first). */\n toLowerCase,\n /** Coerces a value to a number, rejecting empty/whitespace-only strings instead of silently resolving to `0`. */\n toNumber,\n /** Compiles a value into a `RegExp`. */\n toRegExp,\n /** Coerces a value to a string, treating nullish values as `\"\"`. */\n toString,\n /** Parses a value into a `URL`, treating nullish values as `\"\"` first. */\n toURL,\n /** Uppercases a string (coercing nullish values to `\"\"` first). */\n toUpperCase,\n /** Trims surrounding whitespace from a string (coercing nullish values to `\"\"` first). */\n trim,\n}\n/** Convenience {@link runtime.Validator} implementations for common validation patterns (e.g. `validators.range(1, 65535)`). */\nexport const validators = {\n /** Passes when the date value is strictly after `date`. */\n after,\n /** Passes only when every wrapped validator passes; returns the first failing message. */\n all,\n /** Passes when at least one wrapped validator passes. */\n any,\n /** Passes when the date value is strictly before `date`. */\n before,\n /** Identity wrapper for a hand-written `Validator<T>` -- no behavior change, just a fluent entry point alongside the other helpers. */\n custom,\n /** Passes for a plausibly-shaped email address (`local@domain`). */\n email,\n /** Passes when the string ends with `suffix`. */\n endsWith,\n /** Alias for `oneOf` -- passes when the value is one of the `allowed` values. */\n enum: oneOf,\n /** Passes when the number is finite (rejects `Infinity`/`-Infinity`/`NaN`). */\n finite,\n /** Passes when the date is strictly after the current time. */\n future,\n /** Passes when the string contains `text` as a substring. */\n includes,\n /** Passes when the number is an integer. */\n integer: integerValidator,\n /** Passes when the string's length is exactly `expected`. */\n length,\n /** Passes when the string matches `pattern`; `message` overrides the default failure text. */\n matches,\n /** Passes when the number is less than or equal to `maximum`. */\n max,\n /** Passes when the array has at most `maximum` items. */\n maxItems,\n /** Passes when the string's length is at most `length`. */\n maxLength,\n /** Passes when the number is greater than or equal to `minimum`. */\n min,\n /** Passes when the array has at least `minimum` items. */\n minItems,\n /** Passes when the string's length is at least `length`. */\n minLength,\n /** Passes when the number is strictly negative. */\n negative,\n /** Inverts a validator: passes when the wrapped validator fails, and vice versa. */\n not,\n /** Passes when the value is one of the `allowed` values. */\n oneOf,\n /** Passes when the value is undefined, otherwise delegates to the provided validator. */\n optional,\n /** Passes when the date is strictly before the current time. */\n past,\n /** Passes when the number is strictly positive. */\n positive,\n /** Passes when the number is within `[minimum, maximum]` inclusive. */\n range,\n /** Runs the wrapped validator but replaces its failure message with `message`. */\n refine,\n /** Alias for `matches` -- passes when the string matches `pattern`. */\n regex: matches,\n /** Passes when the value is defined and, unless `allowEmptyString` is set, non-empty. */\n required,\n /** Passes when the number is a safe integer (`Number.isSafeInteger`). */\n safeInteger,\n /** Passes when every item in the array is unique. */\n unique,\n /** Passes when the string is a valid absolute URL. */\n url: urlValidator,\n /** Passes when the string is a valid UUID of one of the given `versions` (default: any of 1-8). */\n uuid,\n /** Shorthand for `uuid([version])` -- passes only for that exact UUID version. */\n uuidVersion,\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./.dts/helpers/index.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./.dts/helpers/index.js";
|
package/dist/helpers.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
function describe(value){if(value===void 0)return "undefined";if(value===null)return "null";return `a ${typeof value}`}function toArray(separator=",",processors2=[]){return value=>{const items=Array.isArray(value)?value:typeof value==="string"&&value.trim()!==""?value.split(separator).map(item=>item.trim()):[];return items.map(item=>{return processors2.reduce((current,processor)=>processor(current),item)})}}function base64(){return value=>{try{return Buffer.from(String(value),"base64")}catch{throw new Error("Expected a valid base64 value.")}}}function toBigInt(){return value=>{try{return BigInt(String(value))}catch{throw new Error("Expected a bigint value.")}}}function toBoolean(){return value=>{if(typeof value==="boolean")return value;const normalized=String(value).trim().toLowerCase();if(["true","1","yes","on"].includes(normalized))return true;if(["false","0","no","off"].includes(normalized))return false;throw new Error(`Expected a boolean-like value (true/false/1/0/yes/no/on/off), received ${describe(value)}.`)}}function toDate(){return value=>{const result=new Date(String(value));if(Number.isNaN(result.getTime())){throw new Error("Expected a valid date.")}return result}}function toInteger(){return value=>{const parsed=toNumber()(value);if(!Number.isInteger(parsed)){throw new Error("Expected an integer value.")}return parsed}}function parseJSON(){return value=>{if(typeof value!=="string"){if(value===void 0){throw new Error("Expected a JSON string, received undefined.")}return value}try{return JSON.parse(value)}catch{throw new Error("Expected a valid JSON string, but parsing failed.")}}}function toLowerCase(){return value=>String(value??"").toLowerCase()}function toNumber(){return value=>{if(typeof value==="string"&&value.trim()===""){throw new Error("Expected a numeric value, received an empty string.")}const result=typeof value==="number"?value:Number(value);if(Number.isNaN(result)){throw new Error(`Expected a numeric value, received ${describe(value)}.`)}return result}}function toRegExp(){return value=>{try{return new RegExp(String(value))}catch{throw new Error("Expected a valid regular expression.")}}}function split(separator=","){return value=>{if(typeof value!=="string")return [];return value.split(separator).map(item=>item.trim())}}function toString(){return value=>{if(value===void 0||value===null)return "";return typeof value==="string"?value:String(value)}}function trim(){return value=>String(value??"").trim()}function toUpperCase(){return value=>String(value??"").toUpperCase()}function toURL(){return value=>{try{return new URL(String(value??""))}catch{throw new Error("Expected a valid absolute URL.")}}}function after(date){return (value,_rawEnv)=>value>date||"Expected date to be after required date."}function all(...validators2){return (value,rawEnv)=>{for(const validator of validators2){const result=validator(value,rawEnv);if(result!==true)return result}return true}}function any(...validators2){return (value,rawEnv)=>{for(const validator of validators2){if(validator(value,rawEnv)===true)return true}return "Value did not satisfy any validation."}}function before(date){return (value,_rawEnv)=>value<date||"Expected date to be before required date."}function custom(validator){return validator}function email(){return matches(/^[^\s@]+@[^\s@]+\.[^\s@]+$/,"Expected a valid email address.")}function endsWith(suffix){return (value,_rawEnv)=>value.endsWith(suffix)||"Expected value to end with required suffix."}function finite(){return (value,_rawEnv)=>Number.isFinite(value)||"Expected a finite number."}function future(){return (value,_rawEnv)=>value>new Date||"Expected a future date."}function includes(text){return (value,_rawEnv)=>value.includes(text)||"Expected value to include required text."}function integer(){return (value,_rawEnv)=>Number.isInteger(value)||"Expected an integer value."}function length(expected){return (value,_rawEnv)=>value.length===expected||`Expected length ${expected}.`}function matches(pattern,message){return (value,_rawEnv)=>pattern.test(value)||(message??"Expected value to match required pattern.")}function max(maximum){return (value,_rawEnv)=>value<=maximum||`Expected a value less than or equal to ${maximum}.`}function maxItems(maximum){return (value,_rawEnv)=>value.length<=maximum||`Expected no more than ${maximum} items.`}function maxLength(length2){return (value,_rawEnv)=>value.length<=length2||`Expected a maximum length of ${length2}.`}function min(minimum){return (value,_rawEnv)=>value>=minimum||`Expected a value greater than or equal to ${minimum}.`}function minItems(minimum){return (value,_rawEnv)=>value.length>=minimum||`Expected at least ${minimum} items.`}function minLength(length2){return (value,_rawEnv)=>value.length>=length2||`Expected a minimum length of ${length2}.`}function negative(){return (value,_rawEnv)=>value<0||"Expected a negative value."}function not(validator){return (value,rawEnv)=>validator(value,rawEnv)===true?"Value must not satisfy this validation.":true}function oneOf(allowed){return (value,_rawEnv)=>allowed.includes(value)||`Expected one of: ${allowed.join(", ")}.`}function optional(validator){return (value,rawEnv)=>{if(value===void 0)return true;return validator(value,rawEnv)}}function past(){return (value,_rawEnv)=>value<new Date||"Expected a past date."}function positive(){return (value,_rawEnv)=>value>0||"Expected a positive value."}function range(minimum,maximum){return (value,_rawEnv)=>value>=minimum&&value<=maximum||`Expected a value greater than or equal to ${minimum} and less than or equal to ${maximum}.`}function refine(validator,message){return (value,rawEnv)=>validator(value,rawEnv)===true?true:message}function required(allowEmptyString=false){return (value,_rawEnv)=>{if(value===void 0||value===null){return "This variable is required."}if(!allowEmptyString&&typeof value==="string"&&value.length===0){return "This variable is required."}return true}}function safeInteger(){return (value,_rawEnv)=>Number.isSafeInteger(value)||"Expected a safe integer."}function unique(){return (value,_rawEnv)=>new Set(value).size===value.length||"Expected all items to be unique."}function url(){return (value,_rawEnv)=>{try{new URL(value);return true}catch{return "Expected a valid absolute URL."}}}function uuid(versions=[1,2,3,4,5,6,7,8]){return (value,_rawEnv)=>{const match=/^([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f])[0-9a-f]{3}-([89ab])[0-9a-f]{3}-([0-9a-f]{12})$/i.exec(value);if(!match){return "Expected a valid UUID ."}const version=Number.parseInt(match[3],16);if(!versions.includes(version)){return `Expected UUID version ${versions.join(", ")}.`}return true}}function uuidVersion(number){return uuid([number])}var processors={base64,parseJSON,split,toArray,toBigInt,toBoolean,toDate,toInteger,toLowerCase,toNumber,toRegExp,toString,toURL,toUpperCase,trim};var validators={after,all,any,before,custom,email,endsWith,enum:oneOf,finite,future,includes,integer,length,matches,max,maxItems,maxLength,min,minItems,minLength,negative,not,oneOf,optional,past,positive,range,refine,regex:matches,required,safeInteger,unique,url,uuid,uuidVersion};
|
|
2
|
+
|
|
3
|
+
export { processors, validators };
|
|
4
|
+
//# sourceMappingURL=helpers.js.map
|
|
5
|
+
//# sourceMappingURL=helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/helpers/processors.ts","../src/helpers/validators.ts","../src/helpers/index.ts"],"names":["processors","validators","length"],"mappings":"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","file":"helpers.js","sourcesContent":["import type { Processor } from \"../runtime/index.js\"\n\n/**\n * Convenience processors. Purely optional sugar over `Processor<T>` --\n * the runtime and generator never import from this module. Error messages\n * describe the *type* of an invalid value, never its content, so a\n * misconfigured secret is never echoed back through a thrown error.\n */\n\nfunction describe(value: unknown): string {\n if (value === undefined) return \"undefined\"\n if (value === null) return \"null\"\n return `a ${typeof value}`\n}\n\n/** Splits a delimited string (or passes through an array) into items, running each through `processors` in order. */\nexport function toArray<T>(\n separator: string | RegExp = \",\",\n processors: Processor<unknown>[] = [],\n): Processor<T[]> {\n return (value) => {\n const items = Array.isArray(value)\n ? value\n : typeof value === \"string\" && value.trim() !== \"\"\n ? value.split(separator).map((item) => item.trim())\n : []\n\n return items.map((item) => {\n return processors.reduce((current, processor) => processor(current), item) as T\n })\n }\n}\n\n/** Decodes a base64 string into a `Buffer`. */\nexport function base64(): Processor<Buffer> {\n return (value) => {\n try {\n return Buffer.from(String(value), \"base64\")\n } catch {\n throw new Error(\"Expected a valid base64 value.\")\n }\n }\n}\n\n/** Coerces a value to a `bigint`. */\nexport function toBigInt(): Processor<bigint> {\n return (value) => {\n try {\n return BigInt(String(value))\n } catch {\n throw new Error(\"Expected a bigint value.\")\n }\n }\n}\n\n/** Coerces common boolean-like strings (`true`/`1`/`yes`/`on`, and their opposites) into a real boolean. */\nexport function toBoolean(): Processor<boolean> {\n return (value) => {\n // Bypassing this fast-path is behaviorally equivalent, not a real gap:\n // `String(true)`/`String(false)` round-trip through the exact\n // \"true\"/\"false\" strings already recognized below, so falling through\n // to the general string-parsing path gives the identical result for a\n // real boolean input either way. Hand-verified: mutating this and\n // running the real suite passes unchanged.\n // Stryker disable next-line ConditionalExpression,StringLiteral\n if (typeof value === \"boolean\") return value\n const normalized = String(value).trim().toLowerCase()\n if ([\"true\", \"1\", \"yes\", \"on\"].includes(normalized)) return true\n if ([\"false\", \"0\", \"no\", \"off\"].includes(normalized)) return false\n throw new Error(\n `Expected a boolean-like value (true/false/1/0/yes/no/on/off), received ${describe(value)}.`,\n )\n }\n}\n\n/** Parses a value into a `Date`. */\nexport function toDate(): Processor<Date> {\n return (value) => {\n const result = new Date(String(value))\n\n if (Number.isNaN(result.getTime())) {\n throw new Error(\"Expected a valid date.\")\n }\n\n return result\n }\n}\n\n/** Coerces a value to a number and requires it to be an integer. */\nexport function toInteger(): Processor<number> {\n return (value) => {\n const parsed = toNumber()(value)\n\n if (!Number.isInteger(parsed)) {\n throw new Error(\"Expected an integer value.\")\n }\n\n return parsed\n }\n}\n\n/** Parses a JSON string; passes non-string values through unchanged. */\nexport function parseJSON<T = unknown>(): Processor<T> {\n return (value) => {\n if (typeof value !== \"string\") {\n if (value === undefined) {\n throw new Error(\"Expected a JSON string, received undefined.\")\n }\n return value as T\n }\n try {\n return JSON.parse(value) as T\n } catch {\n throw new Error(\"Expected a valid JSON string, but parsing failed.\")\n }\n }\n}\n\n/** Lowercases a string (coercing nullish values to `\"\"` first). */\nexport function toLowerCase(): Processor<string> {\n return (value) => String(value ?? \"\").toLowerCase()\n}\n\n/** Coerces a value to a number, rejecting empty/whitespace-only strings instead of silently resolving to `0`. */\nexport function toNumber(): Processor<number> {\n return (value) => {\n // `Number(\"\")` is `0`, not `NaN` -- without this check a blank env var would\n // silently resolve to the number 0 instead of failing, slipping past\n // validators.required() too (that checks the processed value, not the raw string).\n if (typeof value === \"string\" && value.trim() === \"\") {\n throw new Error(\"Expected a numeric value, received an empty string.\")\n }\n // Bypassing this type-check (always going through `Number(value)`) is\n // behaviorally equivalent, not a real gap: `Number()` is idempotent on\n // an already-number input (`Number(x) === x` for every real number,\n // including NaN and -0), so the ternary's two branches always produce\n // the same result. Hand-verified: mutating this and running the real\n // suite passes unchanged.\n // Stryker disable next-line ConditionalExpression,StringLiteral\n const result = typeof value === \"number\" ? value : Number(value)\n if (Number.isNaN(result)) {\n throw new Error(`Expected a numeric value, received ${describe(value)}.`)\n }\n return result\n }\n}\n\n/** Compiles a value into a `RegExp`. */\nexport function toRegExp(): Processor<RegExp> {\n return (value) => {\n try {\n return new RegExp(String(value))\n } catch {\n throw new Error(\"Expected a valid regular expression.\")\n }\n }\n}\n\n/** Splits a string on `separator` into a trimmed string array; returns `[]` for non-string input. */\nexport function split(separator = \",\"): Processor<string[]> {\n return (value) => {\n if (typeof value !== \"string\") return []\n\n return value.split(separator).map((item) => item.trim())\n }\n}\n\n/** Coerces a value to a string, treating nullish values as `\"\"`. */\nexport function toString(): Processor<string> {\n return (value) => {\n if (value === undefined || value === null) return \"\"\n // Bypassing this type-check (always going through `String(value)`) is\n // behaviorally equivalent, not a real gap: `String()` is idempotent on\n // an already-string input. Hand-verified: mutating this and running the\n // real suite passes unchanged.\n // Stryker disable next-line ConditionalExpression,StringLiteral\n return typeof value === \"string\" ? value : String(value)\n }\n}\n\n/** Trims surrounding whitespace from a string (coercing nullish values to `\"\"` first). */\nexport function trim(): Processor<string> {\n return (value) => String(value ?? \"\").trim()\n}\n\n/** Uppercases a string (coercing nullish values to `\"\"` first). */\nexport function toUpperCase(): Processor<string> {\n return (value) => String(value ?? \"\").toUpperCase()\n}\n\n/** Parses a value into a `URL`, treating nullish values as `\"\"` first. */\nexport function toURL(): Processor<URL> {\n return (value) => {\n try {\n // The `\"\"` fallback for nullish input is behaviorally equivalent to\n // any other non-URL-shaped fallback string: `new URL(...)` throws for\n // BOTH \"\" and any garbage string, and both land in the same generic\n // catch below with the same message -- there is no distinguishable\n // outcome. Hand-verified: mutating this and running the real suite\n // passes unchanged.\n // Stryker disable next-line StringLiteral\n return new URL(String(value ?? \"\"))\n } catch {\n throw new Error(\"Expected a valid absolute URL.\")\n }\n }\n}\n","import type { Validator } from \"../runtime/index.js\"\n\n/** Passes when the date value is strictly after `date`. */\nexport function after(date: Date): Validator<Date> {\n return (value, _rawEnv) => value > date || \"Expected date to be after required date.\"\n}\n\n/** Passes only when every wrapped validator passes; returns the first failing message. */\nexport function all<T>(...validators: Validator<T>[]): Validator<T> {\n return (value, rawEnv) => {\n for (const validator of validators) {\n const result = validator(value, rawEnv)\n if (result !== true) return result\n }\n\n return true\n }\n}\n\n/** Passes when at least one wrapped validator passes. */\nexport function any<T>(...validators: Validator<T>[]): Validator<T> {\n return (value, rawEnv) => {\n for (const validator of validators) {\n if (validator(value, rawEnv) === true) return true\n }\n\n return \"Value did not satisfy any validation.\"\n }\n}\n\n/** Passes when the date value is strictly before `date`. */\nexport function before(date: Date): Validator<Date> {\n return (value, _rawEnv) => value < date || \"Expected date to be before required date.\"\n}\n\n/** Identity wrapper for a hand-written `Validator<T>` -- no behavior change, just a fluent entry point alongside the other helpers. */\nexport function custom<T>(validator: Validator<T>): Validator<T> {\n return validator\n}\n\n/** Passes for a plausibly-shaped email address (`local@domain`). */\nexport function email(): Validator<string> {\n return matches(/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/, \"Expected a valid email address.\")\n}\n\n/** Passes when the string ends with `suffix`. */\nexport function endsWith(suffix: string): Validator<string> {\n return (value, _rawEnv) => value.endsWith(suffix) || \"Expected value to end with required suffix.\"\n}\n\n/** Passes when the number is finite (rejects `Infinity`/`-Infinity`/`NaN`). */\nexport function finite(): Validator<number> {\n return (value, _rawEnv) => Number.isFinite(value) || \"Expected a finite number.\"\n}\n\n/** Passes when the date is strictly after the current time. */\nexport function future(): Validator<Date> {\n return (value, _rawEnv) => value > new Date() || \"Expected a future date.\"\n}\n\n/** Passes when the string contains `text` as a substring. */\nexport function includes(text: string): Validator<string> {\n return (value, _rawEnv) => value.includes(text) || \"Expected value to include required text.\"\n}\n\n/** Passes when the number is an integer. */\nexport function integer(): Validator<number> {\n return (value, _rawEnv) => Number.isInteger(value) || \"Expected an integer value.\"\n}\n\n/** Passes when the string's length is exactly `expected`. */\nexport function length(expected: number): Validator<string> {\n return (value, _rawEnv) => value.length === expected || `Expected length ${expected}.`\n}\n\n/** Passes when the string matches `pattern`; `message` overrides the default failure text. */\nexport function matches(pattern: RegExp, message?: string): Validator<string> {\n return (value, _rawEnv) =>\n pattern.test(value) || (message ?? \"Expected value to match required pattern.\")\n}\n\n/** Passes when the number is less than or equal to `maximum`. */\nexport function max(maximum: number): Validator<number> {\n return (value, _rawEnv) =>\n value <= maximum || `Expected a value less than or equal to ${maximum}.`\n}\n\n/** Passes when the array has at most `maximum` items. */\nexport function maxItems<T>(maximum: number): Validator<T[]> {\n return (value, _rawEnv) => value.length <= maximum || `Expected no more than ${maximum} items.`\n}\n\n/** Passes when the string's length is at most `length`. */\nexport function maxLength(length: number): Validator<string> {\n return (value, _rawEnv) => value.length <= length || `Expected a maximum length of ${length}.`\n}\n\n/** Passes when the number is greater than or equal to `minimum`. */\nexport function min(minimum: number): Validator<number> {\n return (value, _rawEnv) =>\n value >= minimum || `Expected a value greater than or equal to ${minimum}.`\n}\n\n/** Passes when the array has at least `minimum` items. */\nexport function minItems<T>(minimum: number): Validator<T[]> {\n return (value, _rawEnv) => value.length >= minimum || `Expected at least ${minimum} items.`\n}\n\n/** Passes when the string's length is at least `length`. */\nexport function minLength(length: number): Validator<string> {\n return (value, _rawEnv) => value.length >= length || `Expected a minimum length of ${length}.`\n}\n\n/** Passes when the number is strictly negative. */\nexport function negative(): Validator<number> {\n return (value, _rawEnv) => value < 0 || \"Expected a negative value.\"\n}\n\n/** Inverts a validator: passes when the wrapped validator fails, and vice versa. */\nexport function not<T>(validator: Validator<T>): Validator<T> {\n return (value, rawEnv) =>\n validator(value, rawEnv) === true ? \"Value must not satisfy this validation.\" : true\n}\n\n/** Passes when the value is one of the `allowed` values. */\nexport function oneOf<T>(allowed: readonly T[]): Validator<T> {\n return (value, _rawEnv) => allowed.includes(value) || `Expected one of: ${allowed.join(\", \")}.`\n}\n\n/** Passes when the value is undefined, otherwise delegates to the provided validator. */\nexport function optional<T>(validator: Validator<T>): Validator<T | undefined> {\n return (value, rawEnv) => {\n if (value === undefined) return true\n return validator(value, rawEnv)\n }\n}\n\n/** Passes when the date is strictly before the current time. */\nexport function past(): Validator<Date> {\n return (value, _rawEnv) => value < new Date() || \"Expected a past date.\"\n}\n\n/** Passes when the number is strictly positive. */\nexport function positive(): Validator<number> {\n return (value, _rawEnv) => value > 0 || \"Expected a positive value.\"\n}\n\n/** Passes when the number is within `[minimum, maximum]` inclusive. */\nexport function range(minimum: number, maximum: number): Validator<number> {\n return (value, _rawEnv) =>\n (value >= minimum && value <= maximum) ||\n `Expected a value greater than or equal to ${minimum} and less than or equal to ${maximum}.`\n}\n\n/** Runs the wrapped validator but replaces its failure message with `message`. */\nexport function refine<T>(validator: Validator<T>, message: string): Validator<T> {\n return (value, rawEnv) => (validator(value, rawEnv) === true ? true : message)\n}\n\n/** Passes when the value is defined and, unless `allowEmptyString` is set, non-empty. */\nexport function required(allowEmptyString = false): Validator<unknown> {\n return (value, _rawEnv) => {\n if (value === undefined || value === null) {\n return \"This variable is required.\"\n }\n\n if (!allowEmptyString && typeof value === \"string\" && value.length === 0) {\n return \"This variable is required.\"\n }\n\n return true\n }\n}\n\n/** Passes when the number is a safe integer (`Number.isSafeInteger`). */\nexport function safeInteger(): Validator<number> {\n return (value, _rawEnv) => Number.isSafeInteger(value) || \"Expected a safe integer.\"\n}\n\n/** Passes when every item in the array is unique. */\nexport function unique<T>(): Validator<T[]> {\n return (value, _rawEnv) =>\n new Set(value).size === value.length || \"Expected all items to be unique.\"\n}\n\n/** Passes when the string is a valid absolute URL. */\nexport function url(): Validator<string> {\n return (value, _rawEnv) => {\n try {\n new URL(value)\n return true\n } catch {\n return \"Expected a valid absolute URL.\"\n }\n }\n}\n\n/** Passes when the string is a valid UUID of one of the given `versions` (default: any of 1-8). */\nexport function uuid(versions: readonly number[] = [1, 2, 3, 4, 5, 6, 7, 8]): Validator<string> {\n return (value, _rawEnv) => {\n const match =\n /^([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f])[0-9a-f]{3}-([89ab])[0-9a-f]{3}-([0-9a-f]{12})$/i.exec(\n value,\n )\n\n if (!match) {\n return \"Expected a valid UUID .\"\n }\n\n const version = Number.parseInt(match[3], 16)\n\n if (!versions.includes(version)) {\n return `Expected UUID version ${versions.join(\", \")}.`\n }\n\n return true\n }\n}\n\n/** Shorthand for `uuid([version])` -- passes only for that exact UUID version. */\nexport function uuidVersion(number: number): Validator<string> {\n return uuid([number])\n}\n","/**\n * Optional convenience helpers for common processor/validator patterns.\n * Entirely separate from the core runtime -- `defineEnv`/`validateEnv` have\n * no knowledge of this module and work identically without it.\n *\n * Usage: `processors.toNumber()`, `validators.range(1, 65535)`.\n *\n * Deliberately built from explicit named imports assembled into plain object\n * literals, not `export * as processors from \"./processors.js\"`. A bundler\n * (tsup/esbuild) lowers that `export * as` form into a namespace-construction\n * helper call it can't prove side-effect-free, which defeats tree-shaking in\n * a *second*, downstream bundling pass (a consumer's own bundler bundling\n * this already-bundled package): importing only `processors` would still\n * ship all of `validators`' code. A plain object literal of already-imported\n * bindings has no such call in the way, so `import { processors } from\n * \"env-cap/helpers\"` alone drops `validators` entirely.\n */\nimport {\n base64,\n parseJSON,\n split,\n toArray,\n toBigInt,\n toBoolean,\n toDate,\n toInteger,\n toLowerCase,\n toNumber,\n toRegExp,\n toString,\n toURL,\n toUpperCase,\n trim,\n} from \"./processors.js\"\nimport {\n after,\n all,\n any,\n before,\n custom,\n email,\n endsWith,\n finite,\n future,\n includes,\n integer as integerValidator,\n length,\n matches,\n max,\n maxItems,\n maxLength,\n min,\n minItems,\n minLength,\n negative,\n not,\n oneOf,\n optional,\n past,\n positive,\n range,\n refine,\n required,\n safeInteger,\n unique,\n url as urlValidator,\n uuid,\n uuidVersion,\n} from \"./validators.js\"\n\n/**\n * Convenience {@link runtime.Processor} implementations for common coercion\n * patterns (e.g. `processors.toNumber()`).\n *\n * @remarks\n * Naming matches `@maverickcer/data-cap`'s `helpers.processors` -- but these\n * throw with a formatted `Error` message on invalid input, where data-cap's\n * silently return `undefined`. Do not assume the same failure mode when\n * moving between packages.\n */\nexport const processors = {\n /** Decodes a base64 string into a `Buffer`. */\n base64,\n /** Parses a JSON string; passes non-string values through unchanged. */\n parseJSON,\n /** Splits a string on `separator` into a trimmed string array; returns `[]` for non-string input. */\n split,\n /** Splits a delimited string (or passes through an array) into items, running each through `processors` in order. */\n toArray,\n /** Coerces a value to a `bigint`. */\n toBigInt,\n /** Coerces common boolean-like strings (`true`/`1`/`yes`/`on`, and their opposites) into a real boolean. */\n toBoolean,\n /** Parses a value into a `Date`. */\n toDate,\n /** Coerces a value to a number and requires it to be an integer. */\n toInteger,\n /** Lowercases a string (coercing nullish values to `\"\"` first). */\n toLowerCase,\n /** Coerces a value to a number, rejecting empty/whitespace-only strings instead of silently resolving to `0`. */\n toNumber,\n /** Compiles a value into a `RegExp`. */\n toRegExp,\n /** Coerces a value to a string, treating nullish values as `\"\"`. */\n toString,\n /** Parses a value into a `URL`, treating nullish values as `\"\"` first. */\n toURL,\n /** Uppercases a string (coercing nullish values to `\"\"` first). */\n toUpperCase,\n /** Trims surrounding whitespace from a string (coercing nullish values to `\"\"` first). */\n trim,\n}\n/** Convenience {@link runtime.Validator} implementations for common validation patterns (e.g. `validators.range(1, 65535)`). */\nexport const validators = {\n /** Passes when the date value is strictly after `date`. */\n after,\n /** Passes only when every wrapped validator passes; returns the first failing message. */\n all,\n /** Passes when at least one wrapped validator passes. */\n any,\n /** Passes when the date value is strictly before `date`. */\n before,\n /** Identity wrapper for a hand-written `Validator<T>` -- no behavior change, just a fluent entry point alongside the other helpers. */\n custom,\n /** Passes for a plausibly-shaped email address (`local@domain`). */\n email,\n /** Passes when the string ends with `suffix`. */\n endsWith,\n /** Alias for `oneOf` -- passes when the value is one of the `allowed` values. */\n enum: oneOf,\n /** Passes when the number is finite (rejects `Infinity`/`-Infinity`/`NaN`). */\n finite,\n /** Passes when the date is strictly after the current time. */\n future,\n /** Passes when the string contains `text` as a substring. */\n includes,\n /** Passes when the number is an integer. */\n integer: integerValidator,\n /** Passes when the string's length is exactly `expected`. */\n length,\n /** Passes when the string matches `pattern`; `message` overrides the default failure text. */\n matches,\n /** Passes when the number is less than or equal to `maximum`. */\n max,\n /** Passes when the array has at most `maximum` items. */\n maxItems,\n /** Passes when the string's length is at most `length`. */\n maxLength,\n /** Passes when the number is greater than or equal to `minimum`. */\n min,\n /** Passes when the array has at least `minimum` items. */\n minItems,\n /** Passes when the string's length is at least `length`. */\n minLength,\n /** Passes when the number is strictly negative. */\n negative,\n /** Inverts a validator: passes when the wrapped validator fails, and vice versa. */\n not,\n /** Passes when the value is one of the `allowed` values. */\n oneOf,\n /** Passes when the value is undefined, otherwise delegates to the provided validator. */\n optional,\n /** Passes when the date is strictly before the current time. */\n past,\n /** Passes when the number is strictly positive. */\n positive,\n /** Passes when the number is within `[minimum, maximum]` inclusive. */\n range,\n /** Runs the wrapped validator but replaces its failure message with `message`. */\n refine,\n /** Alias for `matches` -- passes when the string matches `pattern`. */\n regex: matches,\n /** Passes when the value is defined and, unless `allowEmptyString` is set, non-empty. */\n required,\n /** Passes when the number is a safe integer (`Number.isSafeInteger`). */\n safeInteger,\n /** Passes when every item in the array is unique. */\n unique,\n /** Passes when the string is a valid absolute URL. */\n url: urlValidator,\n /** Passes when the string is a valid UUID of one of the given `versions` (default: any of 1-8). */\n uuid,\n /** Shorthand for `uuid([version])` -- passes only for that exact UUID version. */\n uuidVersion,\n}\n"]}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var state=createState();var contractValues=new Map;var contractErrors=new Map;function createState(){return {status:"uninitialized",contractIds:new Set}}function getState(){return state}function getContractValues(id){return contractValues.get(id)}function setContractValues(id,values){contractValues.set(id,values);}function getContractError(id){return contractErrors.get(id)}function setContractError(id,error){contractErrors.set(id,error);}function resetCache(){for(const id of state.contractIds){contractValues.delete(id);contractErrors.delete(id);}state=createState();}function formatFailure(failure){const heading=failure.kind==="processor"?"Processor failed:":"Validation failed:";const lines=[failure.variable,"-".repeat(Math.max(failure.variable.length,3)),heading,failure.message];lines.push("","Source:",failure.source??failure.contractName);return lines.join("\n")}function formatReport(failures){const count=failures.length;const noun=count===1?"configuration error":"configuration errors";const header=`Environment validation failed.
|
|
4
|
+
|
|
5
|
+
${count} ${noun} found:`;const blocks=failures.map(formatFailure);return [header,...blocks].join("\n\n")}var EnvValidationError=class _EnvValidationError extends Error{constructor(failures){super(formatReport(failures));this.code="ENV_VALIDATION_FAILED";this.name="EnvValidationError";this.failures=failures;Error.captureStackTrace?.(this,_EnvValidationError);}};var EnvNotReadyError=class _EnvNotReadyError extends Error{constructor(contractName,key){const target=`"${contractName}".${key}`;super(`Environment contract ${target} has not been validated yet. Call validateEnv() during application startup before accessing environment values.`);this.code="ENV_NOT_READY";this.name="EnvNotReadyError";Error.captureStackTrace?.(this,_EnvNotReadyError);}};var internalsByContract=new WeakMap;function registerContract(contract,internals){internalsByContract.set(contract,internals);}function getContractInternals(contract){const internals=internalsByContract.get(contract);if(!internals){throw new TypeError("env-cap: this value was not created by createEnv(). validateEnv() and resetEnvCache() only accept contracts returned from createEnv().")}return internals}function isEnvContract(value){if(typeof value!=="object")return false;if(value===null)return false;return internalsByContract.has(value)}var anonymousCount=0;function createEnv(schema,options={}){const name=options.name??`anonymous-contract-${++anonymousCount}`;const internals={id:Symbol(name),name,source:options.source,schema};const contract={};for(const key of Object.keys(schema)){Object.defineProperty(contract,key,{enumerable:true,configurable:false,get(){const error=getContractError(internals.id);if(error)throw error;const values=getContractValues(internals.id);if(!values||!Object.prototype.hasOwnProperty.call(values,key)){throw new EnvNotReadyError(name,key)}return values[key]}});}Object.defineProperty(contract,Symbol.for("nodejs.util.inspect.custom"),{enumerable:false,value:()=>`EnvContract("${name}") { ${Object.keys(schema).length} variable(s) }`});Object.defineProperty(contract,"toString",{enumerable:false,value:()=>`EnvContract("${name}")`});Object.defineProperty(contract,"toJSON",{enumerable:false,value:()=>`[EnvContract:${name}]`});Object.freeze(contract);registerContract(contract,internals);return contract}function documentEnv(schema,docs){}function resetEnvCache(){resetCache();}async function validateEnv(options){const state2=getState();if(state2.status==="ready"&&state2.result){return state2.result}if(state2.status==="failed"&&state2.error){throw state2.error}if(state2.inFlight){return state2.inFlight}state2.status="validating";const run=runValidation(options,state2);state2.inFlight=run;try{return await run}finally{state2.inFlight=void 0;}}function matchesContext(variableContext,activeContexts){return variableContext===void 0||(activeContexts?.has(variableContext)??false)}async function runValidation(options,state2){const failures=[];let variableCount=0;const activeContexts=options.activeContexts?.length?new Set(options.activeContexts):void 0;const resolved=[];for(const contract of options.manifest){const internals=getContractInternals(contract);const values={};for(const key of Object.keys(internals.schema)){const definition=internals.schema[key];if(!matchesContext(definition.context,activeContexts))continue;variableCount+=1;const raw=options.values[key];let working=raw;if(working===void 0&&definition.default!==void 0){working=typeof definition.default==="function"?definition.default():definition.default;}let processed=working;if(definition.processor){try{processed=definition.processor(working);}catch(cause){failures.push({variable:key,contractName:internals.name,source:internals.source,kind:"processor",message:toMessage(cause)});continue}}if(definition.validator){let outcome;try{outcome=definition.validator(processed,options.values);}catch(cause){outcome=toMessage(cause);}if(outcome!==true){failures.push({variable:key,contractName:internals.name,source:internals.source,kind:"validator",message:outcome});continue}}values[key]=processed;}resolved.push({id:internals.id,values});state2.contractIds.add(internals.id);}if(failures.length>0){const error=new EnvValidationError(failures);for(const{id}of resolved){setContractError(id,error);}state2.status="failed";state2.error=error;throw error}for(const{id,values}of resolved){setContractValues(id,Object.freeze(values));}const result={contractCount:options.manifest.length,variableCount};state2.status="ready";state2.result=result;return result}function toMessage(cause){return cause instanceof Error?cause.message:String(cause)}
|
|
6
|
+
|
|
7
|
+
exports.EnvNotReadyError = EnvNotReadyError;
|
|
8
|
+
exports.EnvValidationError = EnvValidationError;
|
|
9
|
+
exports.createEnv = createEnv;
|
|
10
|
+
exports.documentEnv = documentEnv;
|
|
11
|
+
exports.isEnvContract = isEnvContract;
|
|
12
|
+
exports.resetEnvCache = resetEnvCache;
|
|
13
|
+
exports.validateEnv = validateEnv;
|
|
14
|
+
//# sourceMappingURL=index.cjs.map
|
|
15
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/runtime/cache.ts","../src/runtime/errors.ts","../src/runtime/registry.ts","../src/runtime/create.ts","../src/runtime/document.ts","../src/runtime/reset.ts","../src/runtime/validate.ts"],"names":["state"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { EnvValidationError } from \"./errors.js\"\nimport type { validateEnvResult } from \"./types.js\"\n\ntype CacheStatus = \"uninitialized\" | \"validating\" | \"ready\" | \"failed\"\n\nexport interface CacheState {\n status: CacheStatus\n /** ids of every contract validated as part of this initialization, so resetEnvCache() can clean up precisely. */\n contractIds: Set<symbol>\n result?: validateEnvResult\n error?: EnvValidationError\n inFlight?: Promise<validateEnvResult>\n}\n\n/**\n * All shared state lives here -- nothing is exported except the accessor\n * functions below, and every write goes through them. Values are frozen\n * before they ever reach `contractValues`, so once set, a contract's\n * resolved record cannot be mutated in place.\n *\n * There is a single global state, not one per \"runtime\": a process validates\n * its environment once, at startup. Server and client contexts never share a\n * process (server code and a browser bundle are two separate processes), so\n * there's nothing to key a second state by.\n */\nlet state: CacheState = createState()\nconst contractValues = new Map<symbol, Readonly<Record<string, unknown>>>()\nconst contractErrors = new Map<symbol, EnvValidationError>()\n\nfunction createState(): CacheState {\n return { status: \"uninitialized\", contractIds: new Set() }\n}\n\nexport function getState(): CacheState {\n return state\n}\n\nexport function getContractValues(id: symbol): Readonly<Record<string, unknown>> | undefined {\n return contractValues.get(id)\n}\n\nexport function setContractValues(id: symbol, values: Readonly<Record<string, unknown>>): void {\n contractValues.set(id, values)\n}\n\nexport function getContractError(id: symbol): EnvValidationError | undefined {\n return contractErrors.get(id)\n}\n\nexport function setContractError(id: symbol, error: EnvValidationError): void {\n contractErrors.set(id, error)\n}\n\n/** Clears cached state: the global status plus every contract validated as part of it. */\nexport function resetCache(): void {\n for (const id of state.contractIds) {\n contractValues.delete(id)\n contractErrors.delete(id)\n }\n state = createState()\n}\n","/**\n * Every error type here is constructed only from: the variable name, the\n * declaring contract's name, the failure kind, and a message string that the\n * *developer's own* processor/validator produced. Raw or processed environment\n * values are never read into an error -- see the Security section in the README.\n *\n * Documentation (description, owner, etc.) never appears here, even when a\n * {@link documentEnv} call exists for this variable -- `documentEnv` is\n * build-time-only and its data is never retained anywhere {@link validateEnv}\n * could read it back out. This is a deliberate consequence of keeping the\n * runtime minimal, not an oversight.\n */\n\n/** One variable's processing or validation failure, as recorded by {@link validateEnv}. */\nexport interface VariableFailure {\n /** The schema key that failed. */\n readonly variable: string\n /** The declaring contract's `name` (see {@link CreateEnvOptions}). */\n readonly contractName: string\n /** `import.meta.url` (or similar) passed to `createEnv`'s `source` option, if any. Falls back to `contractName` when absent. */\n readonly source: string | undefined\n /** Which stage produced the failure. */\n readonly kind: \"processor\" | \"validator\"\n /** The error message from the developer's own processor/validator, or the thrown error's message. */\n readonly message: string\n}\n\nfunction formatFailure(failure: VariableFailure): string {\n const heading = failure.kind === \"processor\" ? \"Processor failed:\" : \"Validation failed:\"\n const lines = [\n failure.variable,\n \"-\".repeat(Math.max(failure.variable.length, 3)),\n heading,\n failure.message,\n ]\n\n lines.push(\"\", \"Source:\", failure.source ?? failure.contractName)\n\n return lines.join(\"\\n\")\n}\n\nfunction formatReport(failures: readonly VariableFailure[]): string {\n const count = failures.length\n const noun = count === 1 ? \"configuration error\" : \"configuration errors\"\n const header = `Environment validation failed.\\n\\n${count} ${noun} found:`\n const blocks = failures.map(formatFailure)\n return [header, ...blocks].join(\"\\n\\n\")\n}\n\n/**\n * Thrown by {@link validateEnv} when one or more variables fail processing or\n * validation. Aggregates every failure across every contract in the batch --\n * the caller sees the whole picture in one error, not one-at-a-time.\n *\n * @remarks\n * `code` is a stable, Stable-tier discriminant for programmatic handling --\n * prefer it over `.name`/`instanceof` when a message-independent switch is needed.\n */\nexport class EnvValidationError extends Error {\n /** Stable discriminant for programmatic handling; always `\"ENV_VALIDATION_FAILED\"`. */\n readonly code = \"ENV_VALIDATION_FAILED\"\n /** Every variable failure across every contract in the batch. */\n readonly failures: readonly VariableFailure[]\n\n constructor(failures: readonly VariableFailure[]) {\n super(formatReport(failures))\n this.name = \"EnvValidationError\"\n this.failures = failures\n Error.captureStackTrace?.(this, EnvValidationError)\n }\n}\n\n/**\n * Thrown when a contract's value is accessed via property access before\n * {@link validateEnv} has completed successfully for it.\n *\n * @remarks\n * `code` is a stable, Stable-tier discriminant for programmatic handling --\n * prefer it over `.name`/`instanceof` when a message-independent switch is needed.\n */\nexport class EnvNotReadyError extends Error {\n /** Stable discriminant for programmatic handling; always `\"ENV_NOT_READY\"`. */\n readonly code = \"ENV_NOT_READY\"\n\n constructor(contractName: string, key: string) {\n const target = `\"${contractName}\".${key}`\n super(\n `Environment contract ${target} has not been validated yet. ` +\n \"Call validateEnv() during application startup before accessing environment values.\",\n )\n this.name = \"EnvNotReadyError\"\n Error.captureStackTrace?.(this, EnvNotReadyError)\n }\n}\n","import type { ContractInternals } from \"./types.js\"\n\n/**\n * Private map from a contract object (returned by `createEnv`) to its internal\n * identity/schema. Not exported from the package -- this is the only place\n * that ever associates a contract with its schema, so `validateEnv()`\n * cannot accidentally read across contracts.\n */\nconst internalsByContract = new WeakMap<object, ContractInternals>()\n\n/** Associates a contract object with its internal identity/schema. Called once, by `createEnv`. */\nexport function registerContract(contract: object, internals: ContractInternals): void {\n internalsByContract.set(contract, internals)\n}\n\n/**\n * Looks up a contract's internal identity/schema.\n *\n * @throws {TypeError} If `contract` wasn't returned by `createEnv` (and so was never registered).\n */\nexport function getContractInternals(contract: object): ContractInternals {\n const internals = internalsByContract.get(contract)\n if (!internals) {\n throw new TypeError(\n \"env-cap: this value was not created by createEnv(). \" +\n \"validateEnv() and resetEnvCache() only accept contracts returned from createEnv().\",\n )\n }\n return internals\n}\n\n/** Type guard: `true` when `value` was created by `createEnv` (i.e. is a registered contract). */\nexport function isEnvContract(value: unknown): value is object {\n if (typeof value !== \"object\") return false\n // Split onto its own line (rather than one `&&`-chained expression) so\n // this one check's disable directive can't also silence the still-real,\n // still-tested `typeof` check above. Runtime-redundant on its own --\n // hand-verified: `WeakMap.prototype.has(null)` is spec-guaranteed to\n // return `false` (never throws for a non-object key), so `.has(value)`\n // alone already does the right thing for `null`, the one value where\n // `typeof value === \"object\"` is true but `value` isn't really an object.\n // Kept as a real check anyway because TS needs SOME `null` exclusion here\n // to accept `value` as an `object` below (`typeof value === \"object\"`\n // alone narrows to `object | null`, TS's one special case for `typeof`) --\n // tried a cast/assertion instead (`value as object` / `value!`) and hit an\n // unresolvable conflict in this repo's eslint config: `src/**` bans `!`\n // (`no-non-null-assertion`) while the sibling `non-nullable-type-\n // assertion-style` rule then demands `!` over `as` for a null-only cast.\n // Stryker disable next-line ConditionalExpression\n if (value === null) return false\n return internalsByContract.has(value)\n}\n","import { getContractError, getContractValues } from \"./cache.js\"\nimport { EnvNotReadyError } from \"./errors.js\"\nimport { registerContract } from \"./registry.js\"\nimport type { ContractInternals, CreateEnvOptions, EnvContract, EnvSchema } from \"./types.js\"\n\nlet anonymousCount = 0\n\n/**\n * Declares a feature's environment contract. Colocate this call with the\n * feature that consumes the variables (e.g. `features/payments/env.schema.ts`).\n *\n * @remarks\n * `createEnv` is runtime-only: `processor`/`validator`/`default` are the\n * whole vocabulary, because they're the only fields {@link validateEnv} actually\n * reads. Documentation -- description, ownership, lifecycle, category, and\n * everything else that only exists to generate docs -- lives in a separate\n * {@link documentEnv} call (see `document.ts`), which is entirely optional and\n * never required for this to work.\n *\n * @returns An {@link EnvContract} exposing one read-only getter per key -- there is no\n * global env object, only per-feature contracts like `paymentsEnv.STRIPE_KEY`. Accessing a\n * key throws until {@link validateEnv} has run successfully for the runtime this contract\n * was passed to.\n */\nexport function createEnv<S extends EnvSchema>(\n schema: S,\n options: CreateEnvOptions = {},\n): EnvContract<S> {\n const name = options.name ?? `anonymous-contract-${++anonymousCount}`\n const internals: ContractInternals<S> = { id: Symbol(name), name, source: options.source, schema }\n\n const contract: Record<string, unknown> = {}\n\n for (const key of Object.keys(schema)) {\n Object.defineProperty(contract, key, {\n enumerable: true,\n // Redundant with `Object.freeze(contract)` below (runs after every\n // `defineProperty` call in this function, and forces `configurable:\n // false` on every own property regardless of what was set here) --\n // kept only as the conventional default for a getter no one should\n // reconfigure, not because anything could observe the difference.\n // Stryker disable next-line BooleanLiteral\n configurable: false,\n get(): unknown {\n const error = getContractError(internals.id)\n if (error) throw error\n const values = getContractValues(internals.id)\n // Checking key presence, not just `values` presence, matters once\n // validation contexts exist: a variable whose context didn't match\n // this run's activeContexts is never written into `values` (see\n // validate.ts), so it must throw exactly like an unvalidated\n // contract, not silently resolve to `undefined`.\n if (!values || !Object.prototype.hasOwnProperty.call(values, key)) {\n throw new EnvNotReadyError(name, key)\n }\n return values[key]\n },\n })\n }\n\n // Redact the whole-object view so accidental console.log/JSON.stringify of a\n // contract never prints secret values. Individual keys remain fully readable.\n Object.defineProperty(contract, Symbol.for(\"nodejs.util.inspect.custom\"), {\n enumerable: false,\n value: () => `EnvContract(\"${name}\") { ${Object.keys(schema).length} variable(s) }`,\n })\n Object.defineProperty(contract, \"toString\", {\n enumerable: false,\n value: () => `EnvContract(\"${name}\")`,\n })\n Object.defineProperty(contract, \"toJSON\", {\n enumerable: false,\n value: () => `[EnvContract:${name}]`,\n })\n\n Object.freeze(contract)\n registerContract(contract, internals)\n return contract as EnvContract<S>\n}\n","import type { EnvSchema } from \"./types.js\"\n\n/**\n * Developer-supplied evidence assertions for one variable -- categorically\n * different from every field on {@link VariableDocs}: those are\n * declared-and-never-verified, while `dynamicAccess` is re-checked against\n * reality on every run (fresh / stale / missing). Kept structurally separate,\n * in `VariableDocs.evidence` rather than folded in alongside `description`/\n * `owner`/..., specifically so that different epistemic status is visible in\n * the shape itself and not only in a doc comment. See ADR 0037.\n */\ninterface VariableEvidenceDocs {\n /**\n * Citation(s) of where this variable is actually read dynamically --\n * somewhere env-cap's own static AST scan can't see (a shell script, a\n * Docker entrypoint, a sibling service). Each entry is a\n * `\"<relative-path>:<line>:<column>\"` citation.\n *\n * @remarks\n * A developer's re-acknowledgment that access happens, never a claim\n * env-cap itself observed anything -- tracked as its own independent fact\n * and never folded into the AST-derived `VariableAccessStatus`. Re-verified\n * every run: a citation whose file no longer exists, or whose content has\n * visibly changed since it was last acknowledged, is flagged rather than\n * trusted forever.\n */\n dynamicAccess?: readonly string[]\n}\n\n/**\n * Per-variable documentation. Every field is optional and unconstrained on\n * purpose -- there is no required shape, so documenting a variable never\n * fights the type checker, and you can add a field the generator doesn't\n * know about yet without it being rejected.\n */\nexport interface VariableDocs {\n /** Human-readable explanation of what this variable is and what it controls. */\n description?: string\n /** Who owns this variable (a team, a handle, whatever your org uses). Overrides the contract's own `owner` for this key. */\n owner?: string\n /**\n * How sensitive this variable's value is. Overrides the contract's own `sensitivity` for this\n * key.\n *\n * @remarks\n * Deliberately an open `string`, not a closed union: an org's own sensitivity vocabulary is its\n * own, and a level env-cap doesn't recognize is always honored, never dropped. The generator\n * still reports a `NONSTANDARD_SENSITIVITY_LEVEL` finding (severity `info`, never blocking) for\n * anything outside `secret`/`credential`/`pii`/`config`, so vocabulary drift stays visible\n * without being enforced.\n */\n sensitivity?: string\n /** ISO date string (e.g. \"2026-06-01\") -- when this variable's current value stops being valid (a key rotation deadline, a sunset date, etc.). */\n expiresAt?: string\n /** How to get a new value before/when it expires (e.g. \"Rotate in the Stripe dashboard, then redeploy.\"). */\n refreshInstructions?: string\n /**\n * Explicit, actionable instructions for obtaining this variable's value the *first* time --\n * where `refreshInstructions` is \"how to rotate it once you already have one,\" this is \"how to\n * get one at all\" (e.g. \"Create a restricted API key in the Stripe dashboard under Developers ->\n * API keys, scoped to read/write Charges.\"). A named field specifically so this can render as\n * its own labeled line in generated docs, rather than requiring a `metadata.setup`-style\n * convention with no dedicated rendering or type checking.\n */\n setupInstructions?: string\n /** Documentation-level assertion that this variable must be set. Independent of how (or whether) a validator actually enforces it. */\n required?: boolean\n /** Marks this variable as being phased out. Independent of `expiresAt` (a rotation/sunset date) and of the contract-level `active` switch (on/off, not a phase-out signal). */\n deprecated?: boolean\n /** Why this variable is deprecated, and/or what to use instead. Only meaningful alongside `deprecated: true`. */\n deprecatedReason?: string\n /** ISO date string -- by when a deprecated variable must be removed. Only meaningful alongside `deprecated: true`. */\n removeBy?: string\n /** The previous environment variable name this one replaces, if this declaration is the result of a rename. Lets the Change Model correlate a remove+add pair into a single rename entry instead of two unrelated changes. */\n renamedFrom?: string\n /** Why this variable's value is collected/used -- a framework-agnostic fact (pairs with `legalBasis`; a specific citation like a GDPR article belongs in `metadata` instead). Overrides the contract's own `purpose` for this key. */\n purpose?: string\n /** The legal basis this variable's collection/use relies on -- framework-agnostic (e.g. \"user consent,\" \"contractual necessity\"), never a specific statute name. Overrides the contract's own `legalBasis` for this key. */\n legalBasis?: string\n /** Descriptive retention policy (e.g. \"delete after 90 days\"). A policy statement, not a computed value -- unlike `expiresAt`, nothing parses or evaluates this. Overrides the contract's own `retention` for this key. */\n retention?: string\n /** Where this variable's value is/must be stored (a region, or a permitted set of regions). Overrides the contract's own `dataResidency` for this key. */\n dataResidency?: string | string[]\n /** Documentation-level assertion that this variable's handling must be auditable. Overrides the contract's own `auditRequired` for this key. */\n auditRequired?: boolean\n /**\n * Developer-supplied evidence assertions for this variable -- verified over\n * time, unlike every other field above, which is declared and never\n * verified. Kept structurally separate for exactly that reason; see\n * {@link VariableEvidenceDocs}.\n */\n evidence?: VariableEvidenceDocs\n /**\n * Structured, unsupported-key documentation -- any primitive or object value, for anything that\n * doesn't warrant its own named field. A previously-supported top-level key belongs in a named\n * field above instead of here once one exists for it.\n */\n metadata?: Record<string, unknown>\n}\n\n/**\n * Contract-level documentation, plus everything {@link documentEnv}'s options used to carry that\n * only the generator ever read.\n *\n * @remarks\n * Generic over `S`, the exact schema type {@link documentEnv} infers from its `schema` argument --\n * this is what makes `variables` below a closed, checked map (a typo'd or renamed key is a\n * compile error, not a silent no-op) rather than an open `Record<string, VariableDocs>` any string\n * would satisfy. `S` defaults to the widest possible schema so this type is still nameable on its\n * own (e.g. in a helper function's own parameter type) without narrowing to one specific contract.\n */\nexport interface ContractDocs<S extends EnvSchema = EnvSchema> {\n /** Overrides the auto-generated label used in generated docs. Purely cosmetic. Wins over `createEnv()`'s own `name` option, which in turn wins over the exported binding name. */\n name?: string\n /** Groups this contract under a heading in the generated docs' feature catalog. */\n category?: string\n /** The generator throws if two *active* contracts declare the same `exclusiveGroup` -- use it to mark interchangeable features (e.g. two database backends) as mutually exclusive. */\n exclusiveGroup?: string\n /** When `false`, this contract is excluded from the generated manifest, docs' required section, and exclusiveGroup checks -- it still appears, marked disabled, in the feature catalog. Defaults to `true`. */\n active?: boolean\n /** Default owner for every variable in this contract that doesn't set its own `owner`. */\n owner?: string\n /** Default sensitivity for every variable in this contract that doesn't set its own `sensitivity` -- see {@link VariableDocs.sensitivity} for why this is an open `string`. */\n sensitivity?: string\n /** Whole-contract/feature sunset date, ISO date string. */\n expiresAt?: string\n /** Marks this whole contract/feature as being phased out. Independent of `active` (on/off, not a phase-out signal). */\n deprecated?: boolean\n /** Why this contract is deprecated, and/or what to use instead. Only meaningful alongside `deprecated: true`. */\n deprecatedReason?: string\n /** Default reason this contract's variables' values are collected/used -- see {@link VariableDocs.purpose}. */\n purpose?: string\n /** Default legal basis for this contract's variables -- see {@link VariableDocs.legalBasis}. */\n legalBasis?: string\n /** Default retention policy for this contract's variables -- see {@link VariableDocs.retention}. */\n retention?: string\n /** Default data residency for this contract's variables -- see {@link VariableDocs.dataResidency}. */\n dataResidency?: string | string[]\n /** Default audit-required assertion for this contract's variables -- see {@link VariableDocs.auditRequired}. */\n auditRequired?: boolean\n /** Arbitrary contract-level documentation (e.g. `runbook`), rendered alongside this feature -- see {@link VariableDocs.metadata}. */\n metadata?: Record<string, unknown>\n /**\n * Per-variable documentation, keyed by variable name -- keys are checked against `S`'s own keys\n * at compile time, so documenting a variable that was renamed or removed from `schema` (or a\n * plain typo) is a type error here, not a silently-ignored entry the generator would otherwise\n * have to report as \"stale\" after the fact. A schema key absent from `variables` entirely is\n * still valid (not every variable needs documentation) and is reported as undocumented by the\n * generator, exactly as before.\n */\n variables?: { readonly [K in keyof S]?: VariableDocs }\n}\n\n/**\n * Documents a schema for the generator: explains values, assigns ownership,\n * and describes lifecycle, feeding the generated docs artifact and\n * `.env.example`. Pass it the *same* schema object given to {@link createEnv}, so\n * the generator can statically link the two and verify every variable is\n * documented -- and so TypeScript can check `docs.variables`' keys against\n * `schema`'s own keys, the same object identity doing double duty for both\n * the generator's static link and the type checker's.\n *\n * @remarks\n * A no-op at runtime by design -- nothing passed here is retained anywhere,\n * and this can never throw, no matter how malformed `schema`/`docs` are.\n * Calling it is entirely optional: {@link createEnv} works identically whether or\n * not a matching `documentEnv` call exists. The real \"is everything\n * documented?\" check, and every artifact this data drives (the docs\n * artifact's ownership matrix, dependency graph, lifecycle report, and\n * security review; `.env.example`'s comments), runs entirely inside\n * {@link build.generateEnvManifest}'s static analysis -- this function's only job at\n * runtime is to exist as a safe, harmless marker the AST parser can find,\n * and to give you type-checked argument shapes while writing it.\n *\n * `S` is inferred from `schema`, never written out by hand -- pass the\n * schema object literal (or a `const`-inferred reference to it) directly for\n * the strongest inference; an explicitly-widened `schema: EnvSchema`\n * annotation loses the per-key literal type and falls back to accepting any\n * string key in `docs.variables`, same as before this generic existed.\n */\n// Deliberately a true no-op (see the doc comment above): `documentEnv` has no\n// runtime behavior at all, only a compile-time keyed-shape check and a\n// static-analysis-visible call site -- its body being empty is observably\n// identical to explicitly discarding both arguments, so there is nothing a\n// test could assert to distinguish the two. (A `next-line` disable placed\n// inside the parameter list, right before the closing paren, does not\n// reliably attach as the block's own leading comment -- hence this unscoped\n// disable/restore pair instead; see data-cap's `core/document.ts` for the\n// same pattern and the Stryker directive-attachment mechanics behind it.)\n// Stryker disable BlockStatement\nexport function documentEnv<S extends EnvSchema>(schema: S, docs: ContractDocs<S>): void {\n // Intentionally inert -- see the module doc comment above. Do not add\n // logic here; anything this function does happens at runtime, in every\n // process that imports the schema file, which is exactly what this split\n // exists to avoid. The `void`s below only satisfy `noUnusedParameters` --\n // a bare `schema`/`docs` expression statement trips `no-unused-expressions`\n // instead, and renaming these public parameters to `_schema`/`_docs` would\n // leak into every consumer's editor hover.\n // eslint-disable-next-line @typescript-eslint/no-meaningless-void-operator\n void schema\n // eslint-disable-next-line @typescript-eslint/no-meaningless-void-operator\n void docs\n}\n// Stryker restore BlockStatement\n","import { resetCache } from \"./cache.js\"\n\n/**\n * Clears cached validation state. Intended for tests and dev tooling (e.g.\n * `beforeEach(() => resetEnvCache())` in a test suite that re-validates with\n * different fixture values per test) -- production applications validate\n * once at startup and should not normally call this.\n */\nexport function resetEnvCache(): void {\n resetCache()\n}\n","import { getState, setContractError, setContractValues } from \"./cache.js\"\nimport type { CacheState } from \"./cache.js\"\nimport { EnvValidationError } from \"./errors.js\"\nimport type { VariableFailure } from \"./errors.js\"\nimport { getContractInternals } from \"./registry.js\"\nimport type { validateEnvOptions, validateEnvResult } from \"./types.js\"\n\n/**\n * Validates every variable in every contract in `options.manifest` against\n * `options.values`, then caches the results.\n *\n * @remarks\n * Idempotent: the first successful (or failed) call is authoritative for the life of the\n * process -- later calls return (or re-throw) that same outcome without re-running any\n * processor or validator. Concurrent in-flight calls share one underlying run.\n *\n * @throws {EnvValidationError} If any variable fails processing or validation.\n */\nexport async function validateEnv(options: validateEnvOptions): Promise<validateEnvResult> {\n const state = getState()\n\n // `result`/`error` are only ever set together with their matching\n // `status` (`runValidation()` below sets `state.status = \"ready\"` and\n // `state.result` in the same statement pair, likewise \"failed\"/`error`),\n // and `resetCache()` always replaces the whole `state` object rather than\n // resetting fields individually -- so checking `status` alone already\n // implies `result`/`error` is set. Load-bearing for TypeScript's own\n // narrowing of `state.result`/`state.error` from `T | undefined` to `T`\n // for the `return`/`throw` below, though -- hand-verified by removing\n // each `&&` clause entirely and running the full `vitest run`: all 1236\n // tests still pass.\n // Stryker disable next-line ConditionalExpression, LogicalOperator\n if (state.status === \"ready\" && state.result) {\n return state.result\n }\n // Stryker disable next-line ConditionalExpression, LogicalOperator\n if (state.status === \"failed\" && state.error) {\n throw state.error\n }\n if (state.inFlight) {\n return state.inFlight\n }\n\n // Genuinely unobservable via any real (all-synchronous, per `Processor`/\n // `Validator`'s own types) caller: `runValidation()` below has no\n // internal `await`, so its entire body -- including overwriting\n // `state.status` to \"ready\" or \"failed\" -- runs to completion\n // synchronously as part of evaluating the very next line, before control\n // ever returns to anything that could observe \"validating\". Kept as an\n // honest, self-documenting state-machine value (and to keep `CacheStatus`\n // meaningful if a future version ever awaits inside `runValidation()`).\n // Hand-verified: replacing the string with \"\" and running the full\n // `vitest run` leaves all 1236 tests passing.\n // Stryker disable next-line StringLiteral\n state.status = \"validating\"\n const run = runValidation(options, state)\n state.inFlight = run\n try {\n return await run\n } finally {\n state.inFlight = undefined\n }\n}\n\n/**\n * Whether a variable participates in a validation run: unset `context`\n * always participates; otherwise it participates only when `activeContexts`\n * contains that exact string (no prefix matching, no wildcards, no\n * hierarchy -- see ADR 0022's Formal Invariants).\n */\nfunction matchesContext(\n variableContext: string | undefined,\n activeContexts: ReadonlySet<string> | undefined,\n): boolean {\n return variableContext === undefined || (activeContexts?.has(variableContext) ?? false)\n}\n\n// `async` is load-bearing here despite no internal `await`: `validateEnv()`\n// assigns this call's return value directly to `state.inFlight` so\n// concurrent callers can share one in-flight run (see its own doc comment).\n// That only works if this genuinely returns a Promise -- a synchronous\n// function returning/throwing a plain value would change both the\n// concurrent-sharing behavior and the timing of a thrown validation error.\n// eslint-disable-next-line @typescript-eslint/require-await\nasync function runValidation(\n options: validateEnvOptions,\n state: CacheState,\n): Promise<validateEnvResult> {\n const failures: VariableFailure[] = []\n let variableCount = 0\n\n // Allocated only when contexts are actually in play, so the (overwhelmingly\n // common, and every pre-existing) zero-context path does no extra work.\n const activeContexts = options.activeContexts?.length\n ? new Set(options.activeContexts)\n : undefined\n\n // A poisoned seed element here would be an accumulator only ever `.push()`ed\n // to (never filtered), reaching the two per-entry loops below\n // (`setContractError`/`setContractValues`) destructured as `{id: undefined,\n // values: undefined}` -- `setContractValues(undefined, Object.freeze(undefined))`\n // neither throws nor collides with any real contract's own symbol-keyed\n // entry, so it's unobservable via any real `createEnv()`/`validateEnv()`\n // consumer. Hand-verified: seeding this with a phantom entry and running\n // the full `vitest run` leaves all 1236 tests passing.\n // Stryker disable next-line ArrayDeclaration\n const resolved: { id: symbol; values: Record<string, unknown> }[] = []\n\n for (const contract of options.manifest) {\n const internals = getContractInternals(contract)\n const values: Record<string, unknown> = {}\n\n for (const key of Object.keys(internals.schema)) {\n const definition = internals.schema[key]\n\n // A variable whose context isn't active is skipped entirely -- no\n // default/processor/validator runs, it's never counted as validated,\n // and it stays out of `values`, so `create.ts`'s getter finds it\n // absent and throws EnvNotReadyError exactly as if this run had\n // never happened for it. See ADR 0022.\n if (!matchesContext(definition.context, activeContexts)) continue\n\n variableCount += 1\n\n const raw = options.values[key]\n\n let working: unknown = raw\n // Bypassing the `!== undefined` clause only matters when `working ===\n // undefined` AND `definition.default` genuinely is `undefined` --\n // `typeof undefined === \"function\"` is false, so `working` is\n // assigned `definition.default` (`undefined`) either way: applying \"no\n // default\" when there wasn't one is identical to not applying it.\n // Hand-verified: replacing the whole clause with `true` and running\n // the full `vitest run` leaves all 1236 tests passing.\n // Stryker disable next-line ConditionalExpression\n if (working === undefined && definition.default !== undefined) {\n working =\n typeof definition.default === \"function\"\n ? (definition.default as () => unknown)()\n : definition.default\n }\n\n let processed: unknown = working\n if (definition.processor) {\n try {\n processed = definition.processor(working)\n } catch (cause) {\n failures.push({\n variable: key,\n contractName: internals.name,\n source: internals.source,\n kind: \"processor\",\n message: toMessage(cause),\n })\n continue\n }\n }\n\n if (definition.validator) {\n let outcome: true | string\n try {\n outcome = definition.validator(processed, options.values)\n } catch (cause) {\n outcome = toMessage(cause)\n }\n if (outcome !== true) {\n failures.push({\n variable: key,\n contractName: internals.name,\n source: internals.source,\n kind: \"validator\",\n message: outcome,\n })\n continue\n }\n }\n\n values[key] = processed\n }\n\n resolved.push({ id: internals.id, values })\n state.contractIds.add(internals.id)\n }\n\n if (failures.length > 0) {\n const error = new EnvValidationError(failures)\n for (const { id } of resolved) {\n setContractError(id, error)\n }\n state.status = \"failed\"\n state.error = error\n throw error\n }\n\n for (const { id, values } of resolved) {\n setContractValues(id, Object.freeze(values))\n }\n\n const result: validateEnvResult = {\n contractCount: options.manifest.length,\n variableCount,\n }\n state.status = \"ready\"\n state.result = result\n return result\n}\n\nfunction toMessage(cause: unknown): string {\n return cause instanceof Error ? cause.message : String(cause)\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./.dts/runtime/index.js";
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./.dts/runtime/index.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
var state=createState();var contractValues=new Map;var contractErrors=new Map;function createState(){return {status:"uninitialized",contractIds:new Set}}function getState(){return state}function getContractValues(id){return contractValues.get(id)}function setContractValues(id,values){contractValues.set(id,values);}function getContractError(id){return contractErrors.get(id)}function setContractError(id,error){contractErrors.set(id,error);}function resetCache(){for(const id of state.contractIds){contractValues.delete(id);contractErrors.delete(id);}state=createState();}function formatFailure(failure){const heading=failure.kind==="processor"?"Processor failed:":"Validation failed:";const lines=[failure.variable,"-".repeat(Math.max(failure.variable.length,3)),heading,failure.message];lines.push("","Source:",failure.source??failure.contractName);return lines.join("\n")}function formatReport(failures){const count=failures.length;const noun=count===1?"configuration error":"configuration errors";const header=`Environment validation failed.
|
|
2
|
+
|
|
3
|
+
${count} ${noun} found:`;const blocks=failures.map(formatFailure);return [header,...blocks].join("\n\n")}var EnvValidationError=class _EnvValidationError extends Error{constructor(failures){super(formatReport(failures));this.code="ENV_VALIDATION_FAILED";this.name="EnvValidationError";this.failures=failures;Error.captureStackTrace?.(this,_EnvValidationError);}};var EnvNotReadyError=class _EnvNotReadyError extends Error{constructor(contractName,key){const target=`"${contractName}".${key}`;super(`Environment contract ${target} has not been validated yet. Call validateEnv() during application startup before accessing environment values.`);this.code="ENV_NOT_READY";this.name="EnvNotReadyError";Error.captureStackTrace?.(this,_EnvNotReadyError);}};var internalsByContract=new WeakMap;function registerContract(contract,internals){internalsByContract.set(contract,internals);}function getContractInternals(contract){const internals=internalsByContract.get(contract);if(!internals){throw new TypeError("env-cap: this value was not created by createEnv(). validateEnv() and resetEnvCache() only accept contracts returned from createEnv().")}return internals}function isEnvContract(value){if(typeof value!=="object")return false;if(value===null)return false;return internalsByContract.has(value)}var anonymousCount=0;function createEnv(schema,options={}){const name=options.name??`anonymous-contract-${++anonymousCount}`;const internals={id:Symbol(name),name,source:options.source,schema};const contract={};for(const key of Object.keys(schema)){Object.defineProperty(contract,key,{enumerable:true,configurable:false,get(){const error=getContractError(internals.id);if(error)throw error;const values=getContractValues(internals.id);if(!values||!Object.prototype.hasOwnProperty.call(values,key)){throw new EnvNotReadyError(name,key)}return values[key]}});}Object.defineProperty(contract,Symbol.for("nodejs.util.inspect.custom"),{enumerable:false,value:()=>`EnvContract("${name}") { ${Object.keys(schema).length} variable(s) }`});Object.defineProperty(contract,"toString",{enumerable:false,value:()=>`EnvContract("${name}")`});Object.defineProperty(contract,"toJSON",{enumerable:false,value:()=>`[EnvContract:${name}]`});Object.freeze(contract);registerContract(contract,internals);return contract}function documentEnv(schema,docs){}function resetEnvCache(){resetCache();}async function validateEnv(options){const state2=getState();if(state2.status==="ready"&&state2.result){return state2.result}if(state2.status==="failed"&&state2.error){throw state2.error}if(state2.inFlight){return state2.inFlight}state2.status="validating";const run=runValidation(options,state2);state2.inFlight=run;try{return await run}finally{state2.inFlight=void 0;}}function matchesContext(variableContext,activeContexts){return variableContext===void 0||(activeContexts?.has(variableContext)??false)}async function runValidation(options,state2){const failures=[];let variableCount=0;const activeContexts=options.activeContexts?.length?new Set(options.activeContexts):void 0;const resolved=[];for(const contract of options.manifest){const internals=getContractInternals(contract);const values={};for(const key of Object.keys(internals.schema)){const definition=internals.schema[key];if(!matchesContext(definition.context,activeContexts))continue;variableCount+=1;const raw=options.values[key];let working=raw;if(working===void 0&&definition.default!==void 0){working=typeof definition.default==="function"?definition.default():definition.default;}let processed=working;if(definition.processor){try{processed=definition.processor(working);}catch(cause){failures.push({variable:key,contractName:internals.name,source:internals.source,kind:"processor",message:toMessage(cause)});continue}}if(definition.validator){let outcome;try{outcome=definition.validator(processed,options.values);}catch(cause){outcome=toMessage(cause);}if(outcome!==true){failures.push({variable:key,contractName:internals.name,source:internals.source,kind:"validator",message:outcome});continue}}values[key]=processed;}resolved.push({id:internals.id,values});state2.contractIds.add(internals.id);}if(failures.length>0){const error=new EnvValidationError(failures);for(const{id}of resolved){setContractError(id,error);}state2.status="failed";state2.error=error;throw error}for(const{id,values}of resolved){setContractValues(id,Object.freeze(values));}const result={contractCount:options.manifest.length,variableCount};state2.status="ready";state2.result=result;return result}function toMessage(cause){return cause instanceof Error?cause.message:String(cause)}
|
|
4
|
+
|
|
5
|
+
export { EnvNotReadyError, EnvValidationError, createEnv, documentEnv, isEnvContract, resetEnvCache, validateEnv };
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/runtime/cache.ts","../src/runtime/errors.ts","../src/runtime/registry.ts","../src/runtime/create.ts","../src/runtime/document.ts","../src/runtime/reset.ts","../src/runtime/validate.ts"],"names":["state"],"mappings":"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","file":"index.js","sourcesContent":["import type { EnvValidationError } from \"./errors.js\"\nimport type { validateEnvResult } from \"./types.js\"\n\ntype CacheStatus = \"uninitialized\" | \"validating\" | \"ready\" | \"failed\"\n\nexport interface CacheState {\n status: CacheStatus\n /** ids of every contract validated as part of this initialization, so resetEnvCache() can clean up precisely. */\n contractIds: Set<symbol>\n result?: validateEnvResult\n error?: EnvValidationError\n inFlight?: Promise<validateEnvResult>\n}\n\n/**\n * All shared state lives here -- nothing is exported except the accessor\n * functions below, and every write goes through them. Values are frozen\n * before they ever reach `contractValues`, so once set, a contract's\n * resolved record cannot be mutated in place.\n *\n * There is a single global state, not one per \"runtime\": a process validates\n * its environment once, at startup. Server and client contexts never share a\n * process (server code and a browser bundle are two separate processes), so\n * there's nothing to key a second state by.\n */\nlet state: CacheState = createState()\nconst contractValues = new Map<symbol, Readonly<Record<string, unknown>>>()\nconst contractErrors = new Map<symbol, EnvValidationError>()\n\nfunction createState(): CacheState {\n return { status: \"uninitialized\", contractIds: new Set() }\n}\n\nexport function getState(): CacheState {\n return state\n}\n\nexport function getContractValues(id: symbol): Readonly<Record<string, unknown>> | undefined {\n return contractValues.get(id)\n}\n\nexport function setContractValues(id: symbol, values: Readonly<Record<string, unknown>>): void {\n contractValues.set(id, values)\n}\n\nexport function getContractError(id: symbol): EnvValidationError | undefined {\n return contractErrors.get(id)\n}\n\nexport function setContractError(id: symbol, error: EnvValidationError): void {\n contractErrors.set(id, error)\n}\n\n/** Clears cached state: the global status plus every contract validated as part of it. */\nexport function resetCache(): void {\n for (const id of state.contractIds) {\n contractValues.delete(id)\n contractErrors.delete(id)\n }\n state = createState()\n}\n","/**\n * Every error type here is constructed only from: the variable name, the\n * declaring contract's name, the failure kind, and a message string that the\n * *developer's own* processor/validator produced. Raw or processed environment\n * values are never read into an error -- see the Security section in the README.\n *\n * Documentation (description, owner, etc.) never appears here, even when a\n * {@link documentEnv} call exists for this variable -- `documentEnv` is\n * build-time-only and its data is never retained anywhere {@link validateEnv}\n * could read it back out. This is a deliberate consequence of keeping the\n * runtime minimal, not an oversight.\n */\n\n/** One variable's processing or validation failure, as recorded by {@link validateEnv}. */\nexport interface VariableFailure {\n /** The schema key that failed. */\n readonly variable: string\n /** The declaring contract's `name` (see {@link CreateEnvOptions}). */\n readonly contractName: string\n /** `import.meta.url` (or similar) passed to `createEnv`'s `source` option, if any. Falls back to `contractName` when absent. */\n readonly source: string | undefined\n /** Which stage produced the failure. */\n readonly kind: \"processor\" | \"validator\"\n /** The error message from the developer's own processor/validator, or the thrown error's message. */\n readonly message: string\n}\n\nfunction formatFailure(failure: VariableFailure): string {\n const heading = failure.kind === \"processor\" ? \"Processor failed:\" : \"Validation failed:\"\n const lines = [\n failure.variable,\n \"-\".repeat(Math.max(failure.variable.length, 3)),\n heading,\n failure.message,\n ]\n\n lines.push(\"\", \"Source:\", failure.source ?? failure.contractName)\n\n return lines.join(\"\\n\")\n}\n\nfunction formatReport(failures: readonly VariableFailure[]): string {\n const count = failures.length\n const noun = count === 1 ? \"configuration error\" : \"configuration errors\"\n const header = `Environment validation failed.\\n\\n${count} ${noun} found:`\n const blocks = failures.map(formatFailure)\n return [header, ...blocks].join(\"\\n\\n\")\n}\n\n/**\n * Thrown by {@link validateEnv} when one or more variables fail processing or\n * validation. Aggregates every failure across every contract in the batch --\n * the caller sees the whole picture in one error, not one-at-a-time.\n *\n * @remarks\n * `code` is a stable, Stable-tier discriminant for programmatic handling --\n * prefer it over `.name`/`instanceof` when a message-independent switch is needed.\n */\nexport class EnvValidationError extends Error {\n /** Stable discriminant for programmatic handling; always `\"ENV_VALIDATION_FAILED\"`. */\n readonly code = \"ENV_VALIDATION_FAILED\"\n /** Every variable failure across every contract in the batch. */\n readonly failures: readonly VariableFailure[]\n\n constructor(failures: readonly VariableFailure[]) {\n super(formatReport(failures))\n this.name = \"EnvValidationError\"\n this.failures = failures\n Error.captureStackTrace?.(this, EnvValidationError)\n }\n}\n\n/**\n * Thrown when a contract's value is accessed via property access before\n * {@link validateEnv} has completed successfully for it.\n *\n * @remarks\n * `code` is a stable, Stable-tier discriminant for programmatic handling --\n * prefer it over `.name`/`instanceof` when a message-independent switch is needed.\n */\nexport class EnvNotReadyError extends Error {\n /** Stable discriminant for programmatic handling; always `\"ENV_NOT_READY\"`. */\n readonly code = \"ENV_NOT_READY\"\n\n constructor(contractName: string, key: string) {\n const target = `\"${contractName}\".${key}`\n super(\n `Environment contract ${target} has not been validated yet. ` +\n \"Call validateEnv() during application startup before accessing environment values.\",\n )\n this.name = \"EnvNotReadyError\"\n Error.captureStackTrace?.(this, EnvNotReadyError)\n }\n}\n","import type { ContractInternals } from \"./types.js\"\n\n/**\n * Private map from a contract object (returned by `createEnv`) to its internal\n * identity/schema. Not exported from the package -- this is the only place\n * that ever associates a contract with its schema, so `validateEnv()`\n * cannot accidentally read across contracts.\n */\nconst internalsByContract = new WeakMap<object, ContractInternals>()\n\n/** Associates a contract object with its internal identity/schema. Called once, by `createEnv`. */\nexport function registerContract(contract: object, internals: ContractInternals): void {\n internalsByContract.set(contract, internals)\n}\n\n/**\n * Looks up a contract's internal identity/schema.\n *\n * @throws {TypeError} If `contract` wasn't returned by `createEnv` (and so was never registered).\n */\nexport function getContractInternals(contract: object): ContractInternals {\n const internals = internalsByContract.get(contract)\n if (!internals) {\n throw new TypeError(\n \"env-cap: this value was not created by createEnv(). \" +\n \"validateEnv() and resetEnvCache() only accept contracts returned from createEnv().\",\n )\n }\n return internals\n}\n\n/** Type guard: `true` when `value` was created by `createEnv` (i.e. is a registered contract). */\nexport function isEnvContract(value: unknown): value is object {\n if (typeof value !== \"object\") return false\n // Split onto its own line (rather than one `&&`-chained expression) so\n // this one check's disable directive can't also silence the still-real,\n // still-tested `typeof` check above. Runtime-redundant on its own --\n // hand-verified: `WeakMap.prototype.has(null)` is spec-guaranteed to\n // return `false` (never throws for a non-object key), so `.has(value)`\n // alone already does the right thing for `null`, the one value where\n // `typeof value === \"object\"` is true but `value` isn't really an object.\n // Kept as a real check anyway because TS needs SOME `null` exclusion here\n // to accept `value` as an `object` below (`typeof value === \"object\"`\n // alone narrows to `object | null`, TS's one special case for `typeof`) --\n // tried a cast/assertion instead (`value as object` / `value!`) and hit an\n // unresolvable conflict in this repo's eslint config: `src/**` bans `!`\n // (`no-non-null-assertion`) while the sibling `non-nullable-type-\n // assertion-style` rule then demands `!` over `as` for a null-only cast.\n // Stryker disable next-line ConditionalExpression\n if (value === null) return false\n return internalsByContract.has(value)\n}\n","import { getContractError, getContractValues } from \"./cache.js\"\nimport { EnvNotReadyError } from \"./errors.js\"\nimport { registerContract } from \"./registry.js\"\nimport type { ContractInternals, CreateEnvOptions, EnvContract, EnvSchema } from \"./types.js\"\n\nlet anonymousCount = 0\n\n/**\n * Declares a feature's environment contract. Colocate this call with the\n * feature that consumes the variables (e.g. `features/payments/env.schema.ts`).\n *\n * @remarks\n * `createEnv` is runtime-only: `processor`/`validator`/`default` are the\n * whole vocabulary, because they're the only fields {@link validateEnv} actually\n * reads. Documentation -- description, ownership, lifecycle, category, and\n * everything else that only exists to generate docs -- lives in a separate\n * {@link documentEnv} call (see `document.ts`), which is entirely optional and\n * never required for this to work.\n *\n * @returns An {@link EnvContract} exposing one read-only getter per key -- there is no\n * global env object, only per-feature contracts like `paymentsEnv.STRIPE_KEY`. Accessing a\n * key throws until {@link validateEnv} has run successfully for the runtime this contract\n * was passed to.\n */\nexport function createEnv<S extends EnvSchema>(\n schema: S,\n options: CreateEnvOptions = {},\n): EnvContract<S> {\n const name = options.name ?? `anonymous-contract-${++anonymousCount}`\n const internals: ContractInternals<S> = { id: Symbol(name), name, source: options.source, schema }\n\n const contract: Record<string, unknown> = {}\n\n for (const key of Object.keys(schema)) {\n Object.defineProperty(contract, key, {\n enumerable: true,\n // Redundant with `Object.freeze(contract)` below (runs after every\n // `defineProperty` call in this function, and forces `configurable:\n // false` on every own property regardless of what was set here) --\n // kept only as the conventional default for a getter no one should\n // reconfigure, not because anything could observe the difference.\n // Stryker disable next-line BooleanLiteral\n configurable: false,\n get(): unknown {\n const error = getContractError(internals.id)\n if (error) throw error\n const values = getContractValues(internals.id)\n // Checking key presence, not just `values` presence, matters once\n // validation contexts exist: a variable whose context didn't match\n // this run's activeContexts is never written into `values` (see\n // validate.ts), so it must throw exactly like an unvalidated\n // contract, not silently resolve to `undefined`.\n if (!values || !Object.prototype.hasOwnProperty.call(values, key)) {\n throw new EnvNotReadyError(name, key)\n }\n return values[key]\n },\n })\n }\n\n // Redact the whole-object view so accidental console.log/JSON.stringify of a\n // contract never prints secret values. Individual keys remain fully readable.\n Object.defineProperty(contract, Symbol.for(\"nodejs.util.inspect.custom\"), {\n enumerable: false,\n value: () => `EnvContract(\"${name}\") { ${Object.keys(schema).length} variable(s) }`,\n })\n Object.defineProperty(contract, \"toString\", {\n enumerable: false,\n value: () => `EnvContract(\"${name}\")`,\n })\n Object.defineProperty(contract, \"toJSON\", {\n enumerable: false,\n value: () => `[EnvContract:${name}]`,\n })\n\n Object.freeze(contract)\n registerContract(contract, internals)\n return contract as EnvContract<S>\n}\n","import type { EnvSchema } from \"./types.js\"\n\n/**\n * Developer-supplied evidence assertions for one variable -- categorically\n * different from every field on {@link VariableDocs}: those are\n * declared-and-never-verified, while `dynamicAccess` is re-checked against\n * reality on every run (fresh / stale / missing). Kept structurally separate,\n * in `VariableDocs.evidence` rather than folded in alongside `description`/\n * `owner`/..., specifically so that different epistemic status is visible in\n * the shape itself and not only in a doc comment. See ADR 0037.\n */\ninterface VariableEvidenceDocs {\n /**\n * Citation(s) of where this variable is actually read dynamically --\n * somewhere env-cap's own static AST scan can't see (a shell script, a\n * Docker entrypoint, a sibling service). Each entry is a\n * `\"<relative-path>:<line>:<column>\"` citation.\n *\n * @remarks\n * A developer's re-acknowledgment that access happens, never a claim\n * env-cap itself observed anything -- tracked as its own independent fact\n * and never folded into the AST-derived `VariableAccessStatus`. Re-verified\n * every run: a citation whose file no longer exists, or whose content has\n * visibly changed since it was last acknowledged, is flagged rather than\n * trusted forever.\n */\n dynamicAccess?: readonly string[]\n}\n\n/**\n * Per-variable documentation. Every field is optional and unconstrained on\n * purpose -- there is no required shape, so documenting a variable never\n * fights the type checker, and you can add a field the generator doesn't\n * know about yet without it being rejected.\n */\nexport interface VariableDocs {\n /** Human-readable explanation of what this variable is and what it controls. */\n description?: string\n /** Who owns this variable (a team, a handle, whatever your org uses). Overrides the contract's own `owner` for this key. */\n owner?: string\n /**\n * How sensitive this variable's value is. Overrides the contract's own `sensitivity` for this\n * key.\n *\n * @remarks\n * Deliberately an open `string`, not a closed union: an org's own sensitivity vocabulary is its\n * own, and a level env-cap doesn't recognize is always honored, never dropped. The generator\n * still reports a `NONSTANDARD_SENSITIVITY_LEVEL` finding (severity `info`, never blocking) for\n * anything outside `secret`/`credential`/`pii`/`config`, so vocabulary drift stays visible\n * without being enforced.\n */\n sensitivity?: string\n /** ISO date string (e.g. \"2026-06-01\") -- when this variable's current value stops being valid (a key rotation deadline, a sunset date, etc.). */\n expiresAt?: string\n /** How to get a new value before/when it expires (e.g. \"Rotate in the Stripe dashboard, then redeploy.\"). */\n refreshInstructions?: string\n /**\n * Explicit, actionable instructions for obtaining this variable's value the *first* time --\n * where `refreshInstructions` is \"how to rotate it once you already have one,\" this is \"how to\n * get one at all\" (e.g. \"Create a restricted API key in the Stripe dashboard under Developers ->\n * API keys, scoped to read/write Charges.\"). A named field specifically so this can render as\n * its own labeled line in generated docs, rather than requiring a `metadata.setup`-style\n * convention with no dedicated rendering or type checking.\n */\n setupInstructions?: string\n /** Documentation-level assertion that this variable must be set. Independent of how (or whether) a validator actually enforces it. */\n required?: boolean\n /** Marks this variable as being phased out. Independent of `expiresAt` (a rotation/sunset date) and of the contract-level `active` switch (on/off, not a phase-out signal). */\n deprecated?: boolean\n /** Why this variable is deprecated, and/or what to use instead. Only meaningful alongside `deprecated: true`. */\n deprecatedReason?: string\n /** ISO date string -- by when a deprecated variable must be removed. Only meaningful alongside `deprecated: true`. */\n removeBy?: string\n /** The previous environment variable name this one replaces, if this declaration is the result of a rename. Lets the Change Model correlate a remove+add pair into a single rename entry instead of two unrelated changes. */\n renamedFrom?: string\n /** Why this variable's value is collected/used -- a framework-agnostic fact (pairs with `legalBasis`; a specific citation like a GDPR article belongs in `metadata` instead). Overrides the contract's own `purpose` for this key. */\n purpose?: string\n /** The legal basis this variable's collection/use relies on -- framework-agnostic (e.g. \"user consent,\" \"contractual necessity\"), never a specific statute name. Overrides the contract's own `legalBasis` for this key. */\n legalBasis?: string\n /** Descriptive retention policy (e.g. \"delete after 90 days\"). A policy statement, not a computed value -- unlike `expiresAt`, nothing parses or evaluates this. Overrides the contract's own `retention` for this key. */\n retention?: string\n /** Where this variable's value is/must be stored (a region, or a permitted set of regions). Overrides the contract's own `dataResidency` for this key. */\n dataResidency?: string | string[]\n /** Documentation-level assertion that this variable's handling must be auditable. Overrides the contract's own `auditRequired` for this key. */\n auditRequired?: boolean\n /**\n * Developer-supplied evidence assertions for this variable -- verified over\n * time, unlike every other field above, which is declared and never\n * verified. Kept structurally separate for exactly that reason; see\n * {@link VariableEvidenceDocs}.\n */\n evidence?: VariableEvidenceDocs\n /**\n * Structured, unsupported-key documentation -- any primitive or object value, for anything that\n * doesn't warrant its own named field. A previously-supported top-level key belongs in a named\n * field above instead of here once one exists for it.\n */\n metadata?: Record<string, unknown>\n}\n\n/**\n * Contract-level documentation, plus everything {@link documentEnv}'s options used to carry that\n * only the generator ever read.\n *\n * @remarks\n * Generic over `S`, the exact schema type {@link documentEnv} infers from its `schema` argument --\n * this is what makes `variables` below a closed, checked map (a typo'd or renamed key is a\n * compile error, not a silent no-op) rather than an open `Record<string, VariableDocs>` any string\n * would satisfy. `S` defaults to the widest possible schema so this type is still nameable on its\n * own (e.g. in a helper function's own parameter type) without narrowing to one specific contract.\n */\nexport interface ContractDocs<S extends EnvSchema = EnvSchema> {\n /** Overrides the auto-generated label used in generated docs. Purely cosmetic. Wins over `createEnv()`'s own `name` option, which in turn wins over the exported binding name. */\n name?: string\n /** Groups this contract under a heading in the generated docs' feature catalog. */\n category?: string\n /** The generator throws if two *active* contracts declare the same `exclusiveGroup` -- use it to mark interchangeable features (e.g. two database backends) as mutually exclusive. */\n exclusiveGroup?: string\n /** When `false`, this contract is excluded from the generated manifest, docs' required section, and exclusiveGroup checks -- it still appears, marked disabled, in the feature catalog. Defaults to `true`. */\n active?: boolean\n /** Default owner for every variable in this contract that doesn't set its own `owner`. */\n owner?: string\n /** Default sensitivity for every variable in this contract that doesn't set its own `sensitivity` -- see {@link VariableDocs.sensitivity} for why this is an open `string`. */\n sensitivity?: string\n /** Whole-contract/feature sunset date, ISO date string. */\n expiresAt?: string\n /** Marks this whole contract/feature as being phased out. Independent of `active` (on/off, not a phase-out signal). */\n deprecated?: boolean\n /** Why this contract is deprecated, and/or what to use instead. Only meaningful alongside `deprecated: true`. */\n deprecatedReason?: string\n /** Default reason this contract's variables' values are collected/used -- see {@link VariableDocs.purpose}. */\n purpose?: string\n /** Default legal basis for this contract's variables -- see {@link VariableDocs.legalBasis}. */\n legalBasis?: string\n /** Default retention policy for this contract's variables -- see {@link VariableDocs.retention}. */\n retention?: string\n /** Default data residency for this contract's variables -- see {@link VariableDocs.dataResidency}. */\n dataResidency?: string | string[]\n /** Default audit-required assertion for this contract's variables -- see {@link VariableDocs.auditRequired}. */\n auditRequired?: boolean\n /** Arbitrary contract-level documentation (e.g. `runbook`), rendered alongside this feature -- see {@link VariableDocs.metadata}. */\n metadata?: Record<string, unknown>\n /**\n * Per-variable documentation, keyed by variable name -- keys are checked against `S`'s own keys\n * at compile time, so documenting a variable that was renamed or removed from `schema` (or a\n * plain typo) is a type error here, not a silently-ignored entry the generator would otherwise\n * have to report as \"stale\" after the fact. A schema key absent from `variables` entirely is\n * still valid (not every variable needs documentation) and is reported as undocumented by the\n * generator, exactly as before.\n */\n variables?: { readonly [K in keyof S]?: VariableDocs }\n}\n\n/**\n * Documents a schema for the generator: explains values, assigns ownership,\n * and describes lifecycle, feeding the generated docs artifact and\n * `.env.example`. Pass it the *same* schema object given to {@link createEnv}, so\n * the generator can statically link the two and verify every variable is\n * documented -- and so TypeScript can check `docs.variables`' keys against\n * `schema`'s own keys, the same object identity doing double duty for both\n * the generator's static link and the type checker's.\n *\n * @remarks\n * A no-op at runtime by design -- nothing passed here is retained anywhere,\n * and this can never throw, no matter how malformed `schema`/`docs` are.\n * Calling it is entirely optional: {@link createEnv} works identically whether or\n * not a matching `documentEnv` call exists. The real \"is everything\n * documented?\" check, and every artifact this data drives (the docs\n * artifact's ownership matrix, dependency graph, lifecycle report, and\n * security review; `.env.example`'s comments), runs entirely inside\n * {@link build.generateEnvManifest}'s static analysis -- this function's only job at\n * runtime is to exist as a safe, harmless marker the AST parser can find,\n * and to give you type-checked argument shapes while writing it.\n *\n * `S` is inferred from `schema`, never written out by hand -- pass the\n * schema object literal (or a `const`-inferred reference to it) directly for\n * the strongest inference; an explicitly-widened `schema: EnvSchema`\n * annotation loses the per-key literal type and falls back to accepting any\n * string key in `docs.variables`, same as before this generic existed.\n */\n// Deliberately a true no-op (see the doc comment above): `documentEnv` has no\n// runtime behavior at all, only a compile-time keyed-shape check and a\n// static-analysis-visible call site -- its body being empty is observably\n// identical to explicitly discarding both arguments, so there is nothing a\n// test could assert to distinguish the two. (A `next-line` disable placed\n// inside the parameter list, right before the closing paren, does not\n// reliably attach as the block's own leading comment -- hence this unscoped\n// disable/restore pair instead; see data-cap's `core/document.ts` for the\n// same pattern and the Stryker directive-attachment mechanics behind it.)\n// Stryker disable BlockStatement\nexport function documentEnv<S extends EnvSchema>(schema: S, docs: ContractDocs<S>): void {\n // Intentionally inert -- see the module doc comment above. Do not add\n // logic here; anything this function does happens at runtime, in every\n // process that imports the schema file, which is exactly what this split\n // exists to avoid. The `void`s below only satisfy `noUnusedParameters` --\n // a bare `schema`/`docs` expression statement trips `no-unused-expressions`\n // instead, and renaming these public parameters to `_schema`/`_docs` would\n // leak into every consumer's editor hover.\n // eslint-disable-next-line @typescript-eslint/no-meaningless-void-operator\n void schema\n // eslint-disable-next-line @typescript-eslint/no-meaningless-void-operator\n void docs\n}\n// Stryker restore BlockStatement\n","import { resetCache } from \"./cache.js\"\n\n/**\n * Clears cached validation state. Intended for tests and dev tooling (e.g.\n * `beforeEach(() => resetEnvCache())` in a test suite that re-validates with\n * different fixture values per test) -- production applications validate\n * once at startup and should not normally call this.\n */\nexport function resetEnvCache(): void {\n resetCache()\n}\n","import { getState, setContractError, setContractValues } from \"./cache.js\"\nimport type { CacheState } from \"./cache.js\"\nimport { EnvValidationError } from \"./errors.js\"\nimport type { VariableFailure } from \"./errors.js\"\nimport { getContractInternals } from \"./registry.js\"\nimport type { validateEnvOptions, validateEnvResult } from \"./types.js\"\n\n/**\n * Validates every variable in every contract in `options.manifest` against\n * `options.values`, then caches the results.\n *\n * @remarks\n * Idempotent: the first successful (or failed) call is authoritative for the life of the\n * process -- later calls return (or re-throw) that same outcome without re-running any\n * processor or validator. Concurrent in-flight calls share one underlying run.\n *\n * @throws {EnvValidationError} If any variable fails processing or validation.\n */\nexport async function validateEnv(options: validateEnvOptions): Promise<validateEnvResult> {\n const state = getState()\n\n // `result`/`error` are only ever set together with their matching\n // `status` (`runValidation()` below sets `state.status = \"ready\"` and\n // `state.result` in the same statement pair, likewise \"failed\"/`error`),\n // and `resetCache()` always replaces the whole `state` object rather than\n // resetting fields individually -- so checking `status` alone already\n // implies `result`/`error` is set. Load-bearing for TypeScript's own\n // narrowing of `state.result`/`state.error` from `T | undefined` to `T`\n // for the `return`/`throw` below, though -- hand-verified by removing\n // each `&&` clause entirely and running the full `vitest run`: all 1236\n // tests still pass.\n // Stryker disable next-line ConditionalExpression, LogicalOperator\n if (state.status === \"ready\" && state.result) {\n return state.result\n }\n // Stryker disable next-line ConditionalExpression, LogicalOperator\n if (state.status === \"failed\" && state.error) {\n throw state.error\n }\n if (state.inFlight) {\n return state.inFlight\n }\n\n // Genuinely unobservable via any real (all-synchronous, per `Processor`/\n // `Validator`'s own types) caller: `runValidation()` below has no\n // internal `await`, so its entire body -- including overwriting\n // `state.status` to \"ready\" or \"failed\" -- runs to completion\n // synchronously as part of evaluating the very next line, before control\n // ever returns to anything that could observe \"validating\". Kept as an\n // honest, self-documenting state-machine value (and to keep `CacheStatus`\n // meaningful if a future version ever awaits inside `runValidation()`).\n // Hand-verified: replacing the string with \"\" and running the full\n // `vitest run` leaves all 1236 tests passing.\n // Stryker disable next-line StringLiteral\n state.status = \"validating\"\n const run = runValidation(options, state)\n state.inFlight = run\n try {\n return await run\n } finally {\n state.inFlight = undefined\n }\n}\n\n/**\n * Whether a variable participates in a validation run: unset `context`\n * always participates; otherwise it participates only when `activeContexts`\n * contains that exact string (no prefix matching, no wildcards, no\n * hierarchy -- see ADR 0022's Formal Invariants).\n */\nfunction matchesContext(\n variableContext: string | undefined,\n activeContexts: ReadonlySet<string> | undefined,\n): boolean {\n return variableContext === undefined || (activeContexts?.has(variableContext) ?? false)\n}\n\n// `async` is load-bearing here despite no internal `await`: `validateEnv()`\n// assigns this call's return value directly to `state.inFlight` so\n// concurrent callers can share one in-flight run (see its own doc comment).\n// That only works if this genuinely returns a Promise -- a synchronous\n// function returning/throwing a plain value would change both the\n// concurrent-sharing behavior and the timing of a thrown validation error.\n// eslint-disable-next-line @typescript-eslint/require-await\nasync function runValidation(\n options: validateEnvOptions,\n state: CacheState,\n): Promise<validateEnvResult> {\n const failures: VariableFailure[] = []\n let variableCount = 0\n\n // Allocated only when contexts are actually in play, so the (overwhelmingly\n // common, and every pre-existing) zero-context path does no extra work.\n const activeContexts = options.activeContexts?.length\n ? new Set(options.activeContexts)\n : undefined\n\n // A poisoned seed element here would be an accumulator only ever `.push()`ed\n // to (never filtered), reaching the two per-entry loops below\n // (`setContractError`/`setContractValues`) destructured as `{id: undefined,\n // values: undefined}` -- `setContractValues(undefined, Object.freeze(undefined))`\n // neither throws nor collides with any real contract's own symbol-keyed\n // entry, so it's unobservable via any real `createEnv()`/`validateEnv()`\n // consumer. Hand-verified: seeding this with a phantom entry and running\n // the full `vitest run` leaves all 1236 tests passing.\n // Stryker disable next-line ArrayDeclaration\n const resolved: { id: symbol; values: Record<string, unknown> }[] = []\n\n for (const contract of options.manifest) {\n const internals = getContractInternals(contract)\n const values: Record<string, unknown> = {}\n\n for (const key of Object.keys(internals.schema)) {\n const definition = internals.schema[key]\n\n // A variable whose context isn't active is skipped entirely -- no\n // default/processor/validator runs, it's never counted as validated,\n // and it stays out of `values`, so `create.ts`'s getter finds it\n // absent and throws EnvNotReadyError exactly as if this run had\n // never happened for it. See ADR 0022.\n if (!matchesContext(definition.context, activeContexts)) continue\n\n variableCount += 1\n\n const raw = options.values[key]\n\n let working: unknown = raw\n // Bypassing the `!== undefined` clause only matters when `working ===\n // undefined` AND `definition.default` genuinely is `undefined` --\n // `typeof undefined === \"function\"` is false, so `working` is\n // assigned `definition.default` (`undefined`) either way: applying \"no\n // default\" when there wasn't one is identical to not applying it.\n // Hand-verified: replacing the whole clause with `true` and running\n // the full `vitest run` leaves all 1236 tests passing.\n // Stryker disable next-line ConditionalExpression\n if (working === undefined && definition.default !== undefined) {\n working =\n typeof definition.default === \"function\"\n ? (definition.default as () => unknown)()\n : definition.default\n }\n\n let processed: unknown = working\n if (definition.processor) {\n try {\n processed = definition.processor(working)\n } catch (cause) {\n failures.push({\n variable: key,\n contractName: internals.name,\n source: internals.source,\n kind: \"processor\",\n message: toMessage(cause),\n })\n continue\n }\n }\n\n if (definition.validator) {\n let outcome: true | string\n try {\n outcome = definition.validator(processed, options.values)\n } catch (cause) {\n outcome = toMessage(cause)\n }\n if (outcome !== true) {\n failures.push({\n variable: key,\n contractName: internals.name,\n source: internals.source,\n kind: \"validator\",\n message: outcome,\n })\n continue\n }\n }\n\n values[key] = processed\n }\n\n resolved.push({ id: internals.id, values })\n state.contractIds.add(internals.id)\n }\n\n if (failures.length > 0) {\n const error = new EnvValidationError(failures)\n for (const { id } of resolved) {\n setContractError(id, error)\n }\n state.status = \"failed\"\n state.error = error\n throw error\n }\n\n for (const { id, values } of resolved) {\n setContractValues(id, Object.freeze(values))\n }\n\n const result: validateEnvResult = {\n contractCount: options.manifest.length,\n variableCount,\n }\n state.status = \"ready\"\n state.result = result\n return result\n}\n\nfunction toMessage(cause: unknown): string {\n return cause instanceof Error ? cause.message : String(cause)\n}\n"]}
|
package/dist/node.cjs
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var fs = require('fs/promises');
|
|
4
|
+
|
|
5
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
6
|
+
|
|
7
|
+
var fs__default = /*#__PURE__*/_interopDefault(fs);
|
|
8
|
+
|
|
9
|
+
function readFile(path,encoding){return fs__default.default.readFile(path,encoding)}function writeFile(path,data,encoding){return fs__default.default.writeFile(path,data,encoding)}async function mkdir(path,options){await fs__default.default.mkdir(path,options);}function readdir(path,options){return fs__default.default.readdir(path,options)}function stat(path){return fs__default.default.stat(path)}function realpath(path){return fs__default.default.realpath(path)}var nodeBuildFileSystem={readFile,writeFile,mkdir,readdir,stat,realpath};
|
|
10
|
+
|
|
11
|
+
exports.nodeBuildFileSystem = nodeBuildFileSystem;
|
|
12
|
+
//# sourceMappingURL=node.cjs.map
|
|
13
|
+
//# sourceMappingURL=node.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/filesystem.ts"],"names":["fs"],"mappings":";;;;;;;;AA0BA,SAAS,QAAA,CAAS,IAAA,CAAc,SAAmC,CACjE,OAAOA,mBAAA,CAAG,QAAA,CAAS,KAAM,QAAQ,CACnC,CAEA,SAAS,SAAA,CAAU,KAAc,IAAA,CAAc,QAAA,CAAiC,CAC9E,OAAOA,oBAAG,SAAA,CAAU,IAAA,CAAM,IAAA,CAAM,QAAQ,CAC1C,CAEA,eAAe,KAAA,CAAM,IAAA,CAAc,QAA6C,CAC9E,MAAMA,oBAAG,KAAA,CAAM,IAAA,CAAM,OAAO,EAC9B,CAEA,SAAS,OAAA,CAAQ,KAAc,OAAA,CAA0D,CACvF,OAAOA,mBAAA,CAAG,QAAQ,IAAA,CAAM,OAAO,CACjC,CAEA,SAAS,IAAA,CAAK,IAAA,CAAmC,CAC/C,OAAOA,mBAAA,CAAG,KAAK,IAAI,CACrB,CAEA,SAAS,SAAS,IAAA,CAA+B,CAC/C,OAAOA,mBAAA,CAAG,SAAS,IAAI,CACzB,CASO,IAAM,oBAAuC,CAClD,QAAA,CACA,UACA,KAAA,CACA,OAAA,CACA,KACA,QACF","file":"node.cjs","sourcesContent":["import fs from \"node:fs/promises\"\nimport type { BuildDirent, BuildFileSystem, BuildStats } from \"../build/types.js\"\n\n/**\n * The concrete `node:fs/promises`-backed {@link BuildFileSystem} the `env-cap`\n * CLI hands to `env-cap/build`. Also re-exported as the public\n * `env-cap/node` entry point (`src/node/index.ts`) for a\n * consumer running the generators from their own Node build script.\n *\n * This is the deliberate injection boundary -- mirrors `repo-contract`'s own\n * `repo-contract.config.ts` (`spawn: crossSpawn, env: process.env`): a\n * **library surface** (`./build`) must not acquire filesystem access\n * implicitly; an **executable-context** entry (this CLI, or `./node`)\n * constructs the adapter and passes it in. `src/cli/**` is the sanctioned\n * executable-source carve-out -- `node:fs` lives only here. See ADR 0040.\n *\n * A thin structural pass-through: each method delegates straight to\n * `node:fs/promises`, whose real `Dirent`/`Stats` already satisfy the\n * minimal {@link BuildFileSystem} `BuildDirent`/`BuildStats` shapes.\n *\n * Named `function` declarations, not an object of arrow properties, so each\n * delegation body is a per-call statement a mutation test can actually reach\n * -- an arrow assigned as a property is only evaluated once, at module load,\n * where Stryker's mutant switch can't flip it (the module-level-`Set` trap\n * `build/discover.ts` calls out, in another form).\n */\nfunction readFile(path: string, encoding: \"utf8\"): Promise<string> {\n return fs.readFile(path, encoding)\n}\n\nfunction writeFile(path: string, data: string, encoding: \"utf8\"): Promise<void> {\n return fs.writeFile(path, data, encoding)\n}\n\nasync function mkdir(path: string, options: { recursive: true }): Promise<void> {\n await fs.mkdir(path, options)\n}\n\nfunction readdir(path: string, options: { withFileTypes: true }): Promise<BuildDirent[]> {\n return fs.readdir(path, options)\n}\n\nfunction stat(path: string): Promise<BuildStats> {\n return fs.stat(path)\n}\n\nfunction realpath(path: string): Promise<string> {\n return fs.realpath(path)\n}\n\n// The `{}` mutant here is a module-load-time (static) mutant: once this\n// module is imported and the const is bound, Stryker's per-mutant switch\n// can't re-run the binding, so the mutant can never actually activate --\n// Stryker itself flags it `static: true`. It is plainly non-equivalent (an\n// empty adapter breaks every consumer), just unkillable by the tool. Each\n// delegating function above is individually mutation-covered.\n// Stryker disable next-line ObjectLiteral\nexport const nodeBuildFileSystem: BuildFileSystem = {\n readFile,\n writeFile,\n mkdir,\n readdir,\n stat,\n realpath,\n}\n"]}
|
package/dist/node.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./.dts/node/index.js";
|
package/dist/node.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./.dts/node/index.js";
|
package/dist/node.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
|
|
3
|
+
function readFile(path,encoding){return fs.readFile(path,encoding)}function writeFile(path,data,encoding){return fs.writeFile(path,data,encoding)}async function mkdir(path,options){await fs.mkdir(path,options);}function readdir(path,options){return fs.readdir(path,options)}function stat(path){return fs.stat(path)}function realpath(path){return fs.realpath(path)}var nodeBuildFileSystem={readFile,writeFile,mkdir,readdir,stat,realpath};
|
|
4
|
+
|
|
5
|
+
export { nodeBuildFileSystem };
|
|
6
|
+
//# sourceMappingURL=node.js.map
|
|
7
|
+
//# sourceMappingURL=node.js.map
|
package/dist/node.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/filesystem.ts"],"names":[],"mappings":";;AA0BA,SAAS,QAAA,CAAS,IAAA,CAAc,SAAmC,CACjE,OAAO,EAAA,CAAG,QAAA,CAAS,KAAM,QAAQ,CACnC,CAEA,SAAS,SAAA,CAAU,KAAc,IAAA,CAAc,QAAA,CAAiC,CAC9E,OAAO,GAAG,SAAA,CAAU,IAAA,CAAM,IAAA,CAAM,QAAQ,CAC1C,CAEA,eAAe,KAAA,CAAM,IAAA,CAAc,QAA6C,CAC9E,MAAM,GAAG,KAAA,CAAM,IAAA,CAAM,OAAO,EAC9B,CAEA,SAAS,OAAA,CAAQ,KAAc,OAAA,CAA0D,CACvF,OAAO,EAAA,CAAG,QAAQ,IAAA,CAAM,OAAO,CACjC,CAEA,SAAS,IAAA,CAAK,IAAA,CAAmC,CAC/C,OAAO,EAAA,CAAG,KAAK,IAAI,CACrB,CAEA,SAAS,SAAS,IAAA,CAA+B,CAC/C,OAAO,EAAA,CAAG,SAAS,IAAI,CACzB,CASO,IAAM,oBAAuC,CAClD,QAAA,CACA,UACA,KAAA,CACA,OAAA,CACA,KACA,QACF","file":"node.js","sourcesContent":["import fs from \"node:fs/promises\"\nimport type { BuildDirent, BuildFileSystem, BuildStats } from \"../build/types.js\"\n\n/**\n * The concrete `node:fs/promises`-backed {@link BuildFileSystem} the `env-cap`\n * CLI hands to `env-cap/build`. Also re-exported as the public\n * `env-cap/node` entry point (`src/node/index.ts`) for a\n * consumer running the generators from their own Node build script.\n *\n * This is the deliberate injection boundary -- mirrors `repo-contract`'s own\n * `repo-contract.config.ts` (`spawn: crossSpawn, env: process.env`): a\n * **library surface** (`./build`) must not acquire filesystem access\n * implicitly; an **executable-context** entry (this CLI, or `./node`)\n * constructs the adapter and passes it in. `src/cli/**` is the sanctioned\n * executable-source carve-out -- `node:fs` lives only here. See ADR 0040.\n *\n * A thin structural pass-through: each method delegates straight to\n * `node:fs/promises`, whose real `Dirent`/`Stats` already satisfy the\n * minimal {@link BuildFileSystem} `BuildDirent`/`BuildStats` shapes.\n *\n * Named `function` declarations, not an object of arrow properties, so each\n * delegation body is a per-call statement a mutation test can actually reach\n * -- an arrow assigned as a property is only evaluated once, at module load,\n * where Stryker's mutant switch can't flip it (the module-level-`Set` trap\n * `build/discover.ts` calls out, in another form).\n */\nfunction readFile(path: string, encoding: \"utf8\"): Promise<string> {\n return fs.readFile(path, encoding)\n}\n\nfunction writeFile(path: string, data: string, encoding: \"utf8\"): Promise<void> {\n return fs.writeFile(path, data, encoding)\n}\n\nasync function mkdir(path: string, options: { recursive: true }): Promise<void> {\n await fs.mkdir(path, options)\n}\n\nfunction readdir(path: string, options: { withFileTypes: true }): Promise<BuildDirent[]> {\n return fs.readdir(path, options)\n}\n\nfunction stat(path: string): Promise<BuildStats> {\n return fs.stat(path)\n}\n\nfunction realpath(path: string): Promise<string> {\n return fs.realpath(path)\n}\n\n// The `{}` mutant here is a module-load-time (static) mutant: once this\n// module is imported and the const is bound, Stryker's per-mutant switch\n// can't re-run the binding, so the mutant can never actually activate --\n// Stryker itself flags it `static: true`. It is plainly non-equivalent (an\n// empty adapter breaks every consumer), just unkillable by the tool. Each\n// delegating function above is individually mutation-covered.\n// Stryker disable next-line ObjectLiteral\nexport const nodeBuildFileSystem: BuildFileSystem = {\n readFile,\n writeFile,\n mkdir,\n readdir,\n stat,\n realpath,\n}\n"]}
|