envapt 7.0.3 → 7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/README.md +4 -4
- package/dist/browser/core/EnvapterBase.mjs.map +1 -1
- package/dist/browser/index.mjs +1 -1
- package/dist/browser/infra/runtime.mjs.map +1 -1
- package/dist/browser/sources/ManualEnvSource.mjs +1 -1
- package/dist/browser/sources/ManualEnvSource.mjs.map +1 -1
- package/dist/browser/sources/PortableSource.mjs +2 -0
- package/dist/browser/sources/PortableSource.mjs.map +1 -0
- package/dist/browser/sources/UnboundEnvSource.mjs +1 -1
- package/dist/browser/sources/UnboundEnvSource.mjs.map +1 -1
- package/dist/browser/sources/WorkerEnvSource.mjs +1 -1
- package/dist/browser/sources/WorkerEnvSource.mjs.map +1 -1
- package/dist/browser/sources/coerce.mjs.map +1 -1
- package/dist/node/core/EnvapterBase.cjs.map +1 -1
- package/dist/node/core/EnvapterBase.mjs.map +1 -1
- package/dist/node/index.cjs +1 -1
- package/dist/node/index.mjs +1 -1
- package/dist/node/infra/runtime.cjs.map +1 -1
- package/dist/node/infra/runtime.mjs.map +1 -1
- package/dist/node/sources/ManualEnvSource.cjs +1 -1
- package/dist/node/sources/ManualEnvSource.cjs.map +1 -1
- package/dist/node/sources/ManualEnvSource.mjs +1 -1
- package/dist/node/sources/ManualEnvSource.mjs.map +1 -1
- package/dist/node/sources/PortableSource.cjs +2 -0
- package/dist/node/sources/PortableSource.cjs.map +1 -0
- package/dist/node/sources/PortableSource.mjs +2 -0
- package/dist/node/sources/PortableSource.mjs.map +1 -0
- package/dist/node/sources/UnboundEnvSource.cjs +1 -1
- package/dist/node/sources/UnboundEnvSource.cjs.map +1 -1
- package/dist/node/sources/UnboundEnvSource.mjs +1 -1
- package/dist/node/sources/UnboundEnvSource.mjs.map +1 -1
- package/dist/node/sources/WorkerEnvSource.cjs +1 -1
- package/dist/node/sources/WorkerEnvSource.cjs.map +1 -1
- package/dist/node/sources/WorkerEnvSource.mjs +1 -1
- package/dist/node/sources/WorkerEnvSource.mjs.map +1 -1
- package/dist/node/sources/coerce.cjs.map +1 -1
- package/dist/node/sources/coerce.mjs.map +1 -1
- package/dist/types/core/EnvapterBase.d.mts +9 -10
- package/dist/types/index.d.mts +3 -2
- package/dist/types/index.portable.d.mts +3 -2
- package/dist/types/sources/ManualEnvSource.d.mts +4 -14
- package/dist/types/sources/PortableSource.d.mts +24 -0
- package/dist/types/sources/WorkerEnvSource.d.mts +4 -14
- package/dist/types/types/Source.d.mts +15 -10
- package/dist/workerd/core/EnvapterBase.mjs.map +1 -1
- package/dist/workerd/index.mjs +1 -1
- package/dist/workerd/infra/runtime.mjs.map +1 -1
- package/dist/workerd/sources/ManualEnvSource.mjs +1 -1
- package/dist/workerd/sources/ManualEnvSource.mjs.map +1 -1
- package/dist/workerd/sources/PortableSource.mjs +2 -0
- package/dist/workerd/sources/PortableSource.mjs.map +1 -0
- package/dist/workerd/sources/UnboundEnvSource.mjs +1 -1
- package/dist/workerd/sources/UnboundEnvSource.mjs.map +1 -1
- package/dist/workerd/sources/WorkerEnvSource.mjs +1 -1
- package/dist/workerd/sources/WorkerEnvSource.mjs.map +1 -1
- package/dist/workerd/sources/coerce.mjs.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# envapt
|
|
2
2
|
|
|
3
|
+
## 7.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Add `PortableSource` and the `Source` type, the v8 names for the no-filesystem source class and the general source type.
|
|
8
|
+
`PortableSource` replaces both `ManualEnvSource` and `WorkerEnvSource`, which are now deprecated subclasses of it, so migrate `new ManualEnvSource(obj)` and `new WorkerEnvSource(env)` to `new PortableSource(...)`. `Source` replaces the now-deprecated `EnvSource` type. `PortableSource`'s constructor accepts any `object`, so a Cloudflare `Env` binding (an interface with no index signature) can be passed directly without a cast. Every deprecated name still works and is removed or renamed in v8.
|
|
9
|
+
|
|
3
10
|
## 7.0.3
|
|
4
11
|
|
|
5
12
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -75,14 +75,14 @@ const port = Envapter.getNumber('PORT', 3000);
|
|
|
75
75
|
const origins = Envapter.getUsing('ALLOWED_ORIGINS', Converters.array(), []);
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
On Cloudflare Workers, `env` is importable at module scope, so bind it once in a config module
|
|
79
|
-
browser
|
|
78
|
+
On Cloudflare Workers, `env` is importable at module scope, so bind it once in a config module, and in
|
|
79
|
+
the browser seed a `PortableSource` from the object your bundler injects.
|
|
80
80
|
|
|
81
81
|
```ts
|
|
82
82
|
import { env } from 'cloudflare:workers';
|
|
83
|
-
import { Envapter,
|
|
83
|
+
import { Envapter, PortableSource } from 'envapt';
|
|
84
84
|
|
|
85
|
-
Envapter.useSource(new
|
|
85
|
+
Envapter.useSource(new PortableSource(env));
|
|
86
86
|
|
|
87
87
|
export const apiToken = Envapter.get('API_TOKEN');
|
|
88
88
|
```
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EnvapterBase.mjs","names":[],"sources":["../../../src/core/EnvapterBase.ts"],"sourcesContent":["import { Validator } from '../engine/Validators';\nimport { debugVerbose, getDebugLevel, setDebugLevel } from '../infra/Debug';\nimport { loadDotenv } from '../infra/Dotenv';\nimport { EnvaptError, EnvaptErrorCodes } from '../infra/Error';\nimport { bindRuntimeFromSource } from '../infra/runtime';\nimport { UnboundEnvSource } from '../sources/UnboundEnvSource';\n\nimport type { DebugLevel } from '../infra/Debug';\nimport type { EnvFileOptions } from '../infra/Dotenv';\nimport type { EnvKeyInput, EnvSource, FileEnvSource } from '../types';\n\n/** @internal */\nexport const EnvaptCache = new Map<string, unknown>();\n\n/** @internal */\nexport abstract class EnvapterBase {\n protected static _envPaths: string[] = ['.env'];\n protected static _envPathsExplicitlySet = false;\n protected static _baseDir: string | undefined = undefined;\n protected static _userDefinedEnvFileOptions: EnvFileOptions = {};\n protected static _strict = false;\n protected static _syncProcessEnv = false;\n // Loader-written keys only (collisions skipped). Refilled on every cache rebuild.\n protected static _dotenvAddedKeys: Set<string> = new Set<string>();\n // Unbound by default so non-Node builds throw NoSourceBound on read until useSource() is called.\n // NodeEnvapter's static block binds NodeEnvSource when referenced, so `import 'envapt'` needs no setup.\n protected static _source: EnvSource = new UnboundEnvSource();\n\n /**\n * Enable or disable strict mode. Default `false`. Setting refreshes the cache so\n * previously-cached converted values get re-evaluated under the new rule.\n */\n static set strict(value: boolean) {\n // Anchored to EnvapterBase: `this._strict` would write an own-property on the subclass that base readers miss.\n EnvapterBase._strict = value;\n // `this`, not EnvapterBase: rebuild via the subclass so its `resolveEffectivePaths` override is honored.\n this.refreshCache();\n }\n\n static get strict(): boolean {\n return EnvapterBase._strict;\n }\n\n /**\n * Set the debug log level. Defaults to `silent`. When unset, reads `ENVAPT_DEBUG` from the\n * bound source on first access; the setter overrides any env-var value. Output goes to stderr\n * on Node (the console elsewhere), prefixed with `[envapt]`.\n */\n static set debug(level: DebugLevel) {\n setDebugLevel(level);\n }\n\n static get debug(): DebugLevel {\n return getDebugLevel();\n }\n\n /**\n * Opt-in mirror of dotenv-loaded keys back to `process.env`. Default `false`.\n *\n * Only keys the loader actually wrote are mirrored, so collision behavior follows\n * `envFileOptions.override`: with the default `false`, pre-existing `process.env` values\n * are preserved; with `true`, the file value wins in both the cache and the mirror.\n *\n * Flipping `false → true` mirrors the existing tracked delta immediately (no cache\n * refresh). Flipping `true → false` is one-way: previously mirrored keys remain in\n * `process.env` until the process exits.\n */\n static set syncProcessEnv(value: boolean) {\n Validator.validateSyncProcessEnv(value);\n const previous = EnvapterBase._syncProcessEnv;\n // Anchored to EnvapterBase: `this._syncProcessEnv` would write an own-property on the subclass that base readers miss.\n EnvapterBase._syncProcessEnv = value;\n if (!previous && value && EnvaptCache.size > 0) this.mirrorToProcessEnv();\n }\n\n static get syncProcessEnv(): boolean {\n return EnvapterBase._syncProcessEnv;\n }\n\n protected static treatAsMissing(value: string | undefined): boolean {\n if (value === undefined || value === '') return true;\n if (EnvapterBase._strict && value.trim() === '') return true;\n return false;\n }\n\n // No baseDir: candidate returned unchanged so the source resolves it against its own default\n // (process.cwd() on Node). Resolution goes through the source to keep this class node-free.\n protected static resolveAgainstBase(candidate: string): string {\n const baseDir = EnvapterBase._baseDir;\n if (baseDir === undefined) return candidate;\n const source = EnvapterBase._source;\n /* v8 ignore next -- @preserve callers are all file-gated, so the source is never bare here */\n if (!source.supportsFiles) return candidate;\n return source.resolvePath(baseDir, candidate);\n }\n\n // File-based config (envPaths/baseDir/configureProfiles) is meaningless without a filesystem;\n // throw instead of silently ignoring it on the browser or Workers. Narrows the source so callers\n // can reach the file capabilities (resolvePath/normalizeBaseDir) after the check.\n protected static assertFileApiSupported(api: string, source: EnvSource): asserts source is FileEnvSource {\n if (!source.supportsFiles) {\n throw new EnvaptError(\n EnvaptErrorCodes.FileApiUnsupported,\n `${api} requires a filesystem-backed source; the bound source does not support .env files.`\n );\n }\n }\n\n // Existence via the bound source instead of fs.existsSync/accessSync: a file \"exists\" when the\n // source can read it.\n protected static sourceFileExists(path: string): boolean {\n const source = EnvapterBase._source;\n /* v8 ignore next -- @preserve every caller is file-gated, so this never sees a bare source */\n if (!source.supportsFiles) return false;\n return source.readFile(path, 'utf8') !== undefined;\n }\n\n protected static refreshCache(): void {\n EnvaptCache.clear();\n EnvapterBase._dotenvAddedKeys = new Set();\n debugVerbose('cache cleared, reloading config');\n void this.config; // getter rebuilds the cache as a side effect\n }\n\n protected static mirrorToProcessEnv(): void {\n if (EnvapterBase._dotenvAddedKeys.size === 0) return;\n const source = EnvapterBase._source;\n /* v8 ignore next -- @preserve dotenv keys only accumulate under a file source, so the delta implies supportsFiles here */\n if (!source.supportsFiles) return;\n const mirrored: Record<string, string> = {};\n for (const key of EnvapterBase._dotenvAddedKeys) {\n const value = EnvaptCache.get(key);\n /* v8 ignore next -- @preserve loader only writes strings; defensive against future cache contents */\n if (typeof value !== 'string') continue;\n mirrored[key] = this.resolveForMirror(key, value);\n debugVerbose(`mirrored ${key} to the ambient environment`);\n }\n source.writeVars(mirrored);\n debugVerbose(`mirrored ${EnvapterBase._dotenvAddedKeys.size} keys to the ambient environment`);\n }\n\n // The template resolver is defined in PrimitiveMethods, and EnvapterBase can't call it without an\n // import cycle, so the mirror expands ${VAR} through this override.\n protected static resolveForMirror(_key: string, value: string): string {\n /* v8 ignore next -- @preserve overridden by PrimitiveMethods on every concrete class */\n return value;\n }\n\n // Default returns the explicit `_envPaths`; EnvironmentMethods overrides to layer the dotenv-flow\n // cascade + configureProfiles when envPaths was never explicitly set.\n protected static resolveEffectivePaths(): string[] {\n /* v8 ignore next -- @preserve */\n return this._envPaths.map((p) => this.resolveAgainstBase(p));\n }\n\n protected static resolveKeyInput(keyInput: EnvKeyInput): { key: string; value: string | undefined } {\n const keys = Array.isArray(keyInput) ? keyInput : [keyInput];\n const normalizedKeys = keys as readonly string[];\n\n if (normalizedKeys.length === 0) {\n throw new EnvaptError(EnvaptErrorCodes.InvalidKeyInput, 'At least one environment key must be provided.');\n }\n\n if (normalizedKeys.some((k) => typeof k !== 'string')) {\n throw new EnvaptError(EnvaptErrorCodes.InvalidKeyInput, 'Environment keys must be strings.');\n }\n\n if (normalizedKeys.some((k) => k.trim() === '')) {\n throw new EnvaptError(EnvaptErrorCodes.InvalidKeyInput, 'Environment keys cannot be empty strings.');\n }\n\n for (const candidate of normalizedKeys) {\n const value = this.config.get(candidate) as string | undefined;\n if (value !== undefined) {\n return { key: candidate, value };\n }\n }\n\n return { key: normalizedKeys[0] as string, value: undefined };\n }\n\n protected static get config(): Map<string, unknown> {\n if (EnvaptCache.size === 0) {\n const source = EnvapterBase._source;\n // Clone so the loader and downstream reads never mutate the source's backing object.\n const isolatedEnv: Record<string, string> = { ...source.readVars() };\n\n let added = new Set<string>();\n // Sources without a filesystem (injected objects on the browser or Workers) skip the\n // .env cascade, profiles, and envPaths; only the readVars() snapshot populates the cache.\n if (source.supportsFiles) {\n debugVerbose(`base dir: ${EnvapterBase._baseDir ?? 'working directory'}`);\n // Outside the try below so a missing configured profile path surfaces its EnvaptError; only dotenv parse errors stay caught.\n const effectivePaths = this.resolveEffectivePaths();\n debugVerbose(\n `effective .env paths: ${effectivePaths.length === 0 ? '(none)' : effectivePaths.join(', ')}`\n );\n try {\n added = loadDotenv({\n ...this._userDefinedEnvFileOptions,\n path: effectivePaths,\n processEnv: isolatedEnv,\n readFile: source.readFile.bind(source)\n });\n } catch {}\n }\n EnvapterBase._dotenvAddedKeys = added;\n for (const [key, value] of Object.entries(isolatedEnv)) EnvaptCache.set(key, value);\n debugVerbose(`cache populated: ${EnvaptCache.size} keys total`);\n if (EnvapterBase._syncProcessEnv) this.mirrorToProcessEnv();\n }\n\n return EnvaptCache;\n }\n\n /**\n * Eagerly load the `.env` cascade now instead of lazily on the first read. Idempotent: a no-op\n * once the cache is built. Useful before mirroring to `process.env` (see {@link syncProcessEnv}),\n * which is what the `envapt/config` side-effect entry does.\n */\n static load(): void {\n void this.config;\n }\n\n /**\n * Bind the environment {@link EnvSource}. On Node the entry binds {@link NodeEnvSource} for you\n * (a `process.env` snapshot plus the `.env` cascade); on the browser or Workers, pass a\n * `ManualEnvSource` / `WorkerEnvSource` (or any `EnvSource`) before reading. Clears and rebuilds\n * the cache.\n */\n static useSource(source: EnvSource): void {\n EnvapterBase._source = source;\n bindRuntimeFromSource(source);\n this.refreshCache();\n }\n\n /**\n * Read an environment variable as its raw string, skipping parsing and conversion.\n */\n getRaw(key: EnvKeyInput): string | undefined {\n return EnvapterBase.resolveKeyInput(key).value;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EnvapterBase.mjs","names":[],"sources":["../../../src/core/EnvapterBase.ts"],"sourcesContent":["import { Validator } from '../engine/Validators';\nimport { debugVerbose, getDebugLevel, setDebugLevel } from '../infra/Debug';\nimport { loadDotenv } from '../infra/Dotenv';\nimport { EnvaptError, EnvaptErrorCodes } from '../infra/Error';\nimport { bindRuntimeFromSource } from '../infra/runtime';\nimport { UnboundEnvSource } from '../sources/UnboundEnvSource';\n\nimport type { DebugLevel } from '../infra/Debug';\nimport type { EnvFileOptions } from '../infra/Dotenv';\nimport type { EnvKeyInput, FileEnvSource, Source } from '../types';\n\n/** @internal */\nexport const EnvaptCache = new Map<string, unknown>();\n\n/** @internal */\nexport abstract class EnvapterBase {\n protected static _envPaths: string[] = ['.env'];\n protected static _envPathsExplicitlySet = false;\n protected static _baseDir: string | undefined = undefined;\n protected static _userDefinedEnvFileOptions: EnvFileOptions = {};\n protected static _strict = false;\n protected static _syncProcessEnv = false;\n // Loader-written keys only (collisions skipped). Refilled on every cache rebuild.\n protected static _dotenvAddedKeys: Set<string> = new Set<string>();\n // Unbound by default so non-Node builds throw NoSourceBound on read until useSource() is called.\n // NodeEnvapter's static block binds NodeEnvSource when referenced, so `import 'envapt'` needs no setup.\n protected static _source: Source = new UnboundEnvSource();\n\n /**\n * Enable or disable strict mode. Default `false`. Setting refreshes the cache so\n * previously-cached converted values get re-evaluated under the new rule.\n */\n static set strict(value: boolean) {\n // Anchored to EnvapterBase: `this._strict` would write an own-property on the subclass that base readers miss.\n EnvapterBase._strict = value;\n // rebuild via `this` so the subclass `resolveEffectivePaths` override is honored (EnvapterBase would skip it).\n this.refreshCache();\n }\n\n static get strict(): boolean {\n return EnvapterBase._strict;\n }\n\n /**\n * Set the debug log level. Defaults to `silent`. When unset, reads `ENVAPT_DEBUG` from the\n * bound source on first access. The setter overrides any env-var value. Output goes to stderr\n * on Node (the console elsewhere), prefixed with `[envapt]`.\n */\n static set debug(level: DebugLevel) {\n setDebugLevel(level);\n }\n\n static get debug(): DebugLevel {\n return getDebugLevel();\n }\n\n /**\n * Opt-in mirror of dotenv-loaded keys back to `process.env`. Default `false`.\n *\n * Only keys the loader actually wrote are mirrored, so collision behavior follows\n * `envFileOptions.override`: with the default `false`, pre-existing `process.env` values\n * are preserved. With `true`, the file value wins in both the cache and the mirror.\n *\n * Flipping `false → true` mirrors the existing tracked delta immediately (no cache\n * refresh). Flipping `true → false` is one-way: previously mirrored keys remain in\n * `process.env` until the process exits.\n */\n static set syncProcessEnv(value: boolean) {\n Validator.validateSyncProcessEnv(value);\n const previous = EnvapterBase._syncProcessEnv;\n // Anchored to EnvapterBase: `this._syncProcessEnv` would write an own-property on the subclass that base readers miss.\n EnvapterBase._syncProcessEnv = value;\n if (!previous && value && EnvaptCache.size > 0) this.mirrorToProcessEnv();\n }\n\n static get syncProcessEnv(): boolean {\n return EnvapterBase._syncProcessEnv;\n }\n\n protected static treatAsMissing(value: string | undefined): boolean {\n if (value === undefined || value === '') return true;\n if (EnvapterBase._strict && value.trim() === '') return true;\n return false;\n }\n\n // No baseDir: candidate returned unchanged so the source resolves it against its own default\n // (process.cwd() on Node). Resolution goes through the source to keep this class node-free.\n protected static resolveAgainstBase(candidate: string): string {\n const baseDir = EnvapterBase._baseDir;\n if (baseDir === undefined) return candidate;\n const source = EnvapterBase._source;\n /* v8 ignore next -- @preserve callers are all file-gated, so the source is never bare here */\n if (!source.supportsFiles) return candidate;\n return source.resolvePath(baseDir, candidate);\n }\n\n // File-based config (envPaths/baseDir/configureProfiles) is meaningless without a filesystem, and\n // it throws instead of silently ignoring it on the browser or Workers. Narrows the source so callers\n // can reach the file capabilities (resolvePath/normalizeBaseDir) after the check.\n protected static assertFileApiSupported(api: string, source: Source): asserts source is FileEnvSource {\n if (!source.supportsFiles) {\n throw new EnvaptError(\n EnvaptErrorCodes.FileApiUnsupported,\n `${api} requires a filesystem-backed source; the bound source does not support .env files.`\n );\n }\n }\n\n // Existence via the bound source instead of fs.existsSync/accessSync: a file \"exists\" when the\n // source can read it.\n protected static sourceFileExists(path: string): boolean {\n const source = EnvapterBase._source;\n /* v8 ignore next -- @preserve every caller is file-gated, so this never sees a bare source */\n if (!source.supportsFiles) return false;\n return source.readFile(path, 'utf8') !== undefined;\n }\n\n protected static refreshCache(): void {\n EnvaptCache.clear();\n EnvapterBase._dotenvAddedKeys = new Set();\n debugVerbose('cache cleared, reloading config');\n void this.config; // getter rebuilds the cache as a side effect\n }\n\n protected static mirrorToProcessEnv(): void {\n if (EnvapterBase._dotenvAddedKeys.size === 0) return;\n const source = EnvapterBase._source;\n /* v8 ignore next -- @preserve dotenv keys only accumulate under a file source, so the delta implies supportsFiles here */\n if (!source.supportsFiles) return;\n const mirrored: Record<string, string> = {};\n for (const key of EnvapterBase._dotenvAddedKeys) {\n const value = EnvaptCache.get(key);\n /* v8 ignore next -- @preserve loader only writes strings, defensive against future cache contents */\n if (typeof value !== 'string') continue;\n mirrored[key] = this.resolveForMirror(key, value);\n debugVerbose(`mirrored ${key} to the ambient environment`);\n }\n source.writeVars(mirrored);\n debugVerbose(`mirrored ${EnvapterBase._dotenvAddedKeys.size} keys to the ambient environment`);\n }\n\n // The template resolver is defined in PrimitiveMethods, and EnvapterBase can't call it without an\n // import cycle, so the mirror expands ${VAR} through this override.\n protected static resolveForMirror(_key: string, value: string): string {\n /* v8 ignore next -- @preserve overridden by PrimitiveMethods on every concrete class */\n return value;\n }\n\n // Default returns the explicit `_envPaths`. EnvironmentMethods overrides to layer the dotenv-flow\n // cascade + configureProfiles when envPaths was never explicitly set.\n protected static resolveEffectivePaths(): string[] {\n /* v8 ignore next -- @preserve */\n return this._envPaths.map((p) => this.resolveAgainstBase(p));\n }\n\n protected static resolveKeyInput(keyInput: EnvKeyInput): { key: string; value: string | undefined } {\n const keys = Array.isArray(keyInput) ? keyInput : [keyInput];\n const normalizedKeys = keys as readonly string[];\n\n if (normalizedKeys.length === 0) {\n throw new EnvaptError(EnvaptErrorCodes.InvalidKeyInput, 'At least one environment key must be provided.');\n }\n\n if (normalizedKeys.some((k) => typeof k !== 'string')) {\n throw new EnvaptError(EnvaptErrorCodes.InvalidKeyInput, 'Environment keys must be strings.');\n }\n\n if (normalizedKeys.some((k) => k.trim() === '')) {\n throw new EnvaptError(EnvaptErrorCodes.InvalidKeyInput, 'Environment keys cannot be empty strings.');\n }\n\n for (const candidate of normalizedKeys) {\n const value = this.config.get(candidate) as string | undefined;\n if (value !== undefined) {\n return { key: candidate, value };\n }\n }\n\n return { key: normalizedKeys[0] as string, value: undefined };\n }\n\n protected static get config(): Map<string, unknown> {\n if (EnvaptCache.size === 0) {\n const source = EnvapterBase._source;\n // Clone so the loader and downstream reads never mutate the source's backing object.\n const isolatedEnv: Record<string, string> = { ...source.readVars() };\n\n let added = new Set<string>();\n // Sources without a filesystem (injected objects on the browser or Workers) skip the\n // .env cascade, profiles, and envPaths. Only the readVars() snapshot populates the cache.\n if (source.supportsFiles) {\n debugVerbose(`base dir: ${EnvapterBase._baseDir ?? 'working directory'}`);\n // Outside the try below so a missing configured profile path surfaces its EnvaptError. Only dotenv parse errors stay caught.\n const effectivePaths = this.resolveEffectivePaths();\n debugVerbose(\n `effective .env paths: ${effectivePaths.length === 0 ? '(none)' : effectivePaths.join(', ')}`\n );\n try {\n added = loadDotenv({\n ...this._userDefinedEnvFileOptions,\n path: effectivePaths,\n processEnv: isolatedEnv,\n readFile: source.readFile.bind(source)\n });\n } catch {}\n }\n EnvapterBase._dotenvAddedKeys = added;\n for (const [key, value] of Object.entries(isolatedEnv)) EnvaptCache.set(key, value);\n debugVerbose(`cache populated: ${EnvaptCache.size} keys total`);\n if (EnvapterBase._syncProcessEnv) this.mirrorToProcessEnv();\n }\n\n return EnvaptCache;\n }\n\n /**\n * Eagerly load the `.env` cascade now instead of lazily on the first read. Idempotent: a no-op\n * once the cache is built. Useful before mirroring to `process.env` (see {@link syncProcessEnv}),\n * which is what the `envapt/config` side-effect entry does.\n */\n static load(): void {\n void this.config;\n }\n\n /**\n * Bind the environment {@link Source}. On Node the entry binds {@link NodeEnvSource} for you\n * (a `process.env` snapshot plus the `.env` cascade). On the browser or Workers, pass a\n * `PortableSource` (or any `Source`) before reading. Clears and rebuilds the cache.\n */\n static useSource(source: Source): void {\n EnvapterBase._source = source;\n bindRuntimeFromSource(source);\n this.refreshCache();\n }\n\n /**\n * Read an environment variable as its raw string, skipping parsing and conversion.\n */\n getRaw(key: EnvKeyInput): string | undefined {\n return EnvapterBase.resolveKeyInput(key).value;\n }\n}\n"],"mappings":"+WAYA,MAAa,EAAc,IAAI,IAG/B,IAAsB,EAAtB,MAAsB,CAAa,CAC/B,OAAiB,UAAsB,CAAC,MAAM,EAC9C,OAAiB,uBAAyB,GAC1C,OAAiB,SAA+B,IAAA,GAChD,OAAiB,2BAA6C,CAAC,EAC/D,OAAiB,QAAU,GAC3B,OAAiB,gBAAkB,GAEnC,OAAiB,iBAAgC,IAAI,IAGrD,OAAiB,QAAkB,IAAI,EAMvC,WAAW,OAAO,EAAgB,CAE9B,EAAa,QAAU,EAEvB,KAAK,aAAa,CACtB,CAEA,WAAW,QAAkB,CACzB,OAAO,EAAa,OACxB,CAOA,WAAW,MAAM,EAAmB,CAChC,EAAc,CAAK,CACvB,CAEA,WAAW,OAAoB,CAC3B,OAAO,EAAc,CACzB,CAaA,WAAW,eAAe,EAAgB,CACtC,EAAU,uBAAuB,CAAK,EACtC,IAAM,EAAW,EAAa,gBAE9B,EAAa,gBAAkB,EAC3B,CAAC,GAAY,GAAS,EAAY,KAAO,GAAG,KAAK,mBAAmB,CAC5E,CAEA,WAAW,gBAA0B,CACjC,OAAO,EAAa,eACxB,CAEA,OAAiB,eAAe,EAAoC,CAGhE,MADA,GADI,IAAU,IAAA,IAAa,IAAU,IACjC,EAAa,SAAW,EAAM,KAAK,IAAM,GAEjD,CAIA,OAAiB,mBAAmB,EAA2B,CAC3D,IAAM,EAAU,EAAa,SAC7B,GAAI,IAAY,IAAA,GAAW,OAAO,EAClC,IAAM,EAAS,EAAa,QAG5B,OADK,EAAO,cACL,EAAO,YAAY,EAAS,CAAS,EADV,CAEtC,CAKA,OAAiB,uBAAuB,EAAa,EAAiD,CAClG,GAAI,CAAC,EAAO,cACR,MAAM,IAAI,EAAA,IAEN,GAAG,EAAI,oFACX,CAER,CAIA,OAAiB,iBAAiB,EAAuB,CACrD,IAAM,EAAS,EAAa,QAG5B,OADK,EAAO,cACL,EAAO,SAAS,EAAM,MAAM,IAAM,IAAA,GADP,EAEtC,CAEA,OAAiB,cAAqB,CAClC,EAAY,MAAM,EAClB,EAAa,iBAAmB,IAAI,IACpC,EAAa,iCAAiC,EAC9C,KAAU,MACd,CAEA,OAAiB,oBAA2B,CACxC,GAAI,EAAa,iBAAiB,OAAS,EAAG,OAC9C,IAAM,EAAS,EAAa,QAE5B,GAAI,CAAC,EAAO,cAAe,OAC3B,IAAM,EAAmC,CAAC,EAC1C,IAAK,IAAM,KAAO,EAAa,iBAAkB,CAC7C,IAAM,EAAQ,EAAY,IAAI,CAAG,EAE7B,OAAO,GAAU,WACrB,EAAS,GAAO,KAAK,iBAAiB,EAAK,CAAK,EAChD,EAAa,YAAY,EAAI,4BAA4B,EAC7D,CACA,EAAO,UAAU,CAAQ,EACzB,EAAa,YAAY,EAAa,iBAAiB,KAAK,iCAAiC,CACjG,CAIA,OAAiB,iBAAiB,EAAc,EAAuB,CAEnE,OAAO,CACX,CAIA,OAAiB,uBAAkC,CAE/C,OAAO,KAAK,UAAU,IAAK,GAAM,KAAK,mBAAmB,CAAC,CAAC,CAC/D,CAEA,OAAiB,gBAAgB,EAAmE,CAEhG,IAAM,EADO,MAAM,QAAQ,CAAQ,EAAI,EAAW,CAAC,CAAQ,EAG3D,GAAI,EAAe,SAAW,EAC1B,MAAM,IAAI,EAAA,IAA8C,gDAAgD,EAG5G,GAAI,EAAe,KAAM,GAAM,OAAO,GAAM,QAAQ,EAChD,MAAM,IAAI,EAAA,IAA8C,mCAAmC,EAG/F,GAAI,EAAe,KAAM,GAAM,EAAE,KAAK,IAAM,EAAE,EAC1C,MAAM,IAAI,EAAA,IAA8C,2CAA2C,EAGvG,IAAK,IAAM,KAAa,EAAgB,CACpC,IAAM,EAAQ,KAAK,OAAO,IAAI,CAAS,EACvC,GAAI,IAAU,IAAA,GACV,MAAO,CAAE,IAAK,EAAW,OAAM,CAEvC,CAEA,MAAO,CAAE,IAAK,EAAe,GAAc,MAAO,IAAA,EAAU,CAChE,CAEA,WAAqB,QAA+B,CAChD,GAAI,EAAY,OAAS,EAAG,CACxB,IAAM,EAAS,EAAa,QAEtB,EAAsC,CAAE,GAAG,EAAO,SAAS,CAAE,EAE/D,EAAQ,IAAI,IAGhB,GAAI,EAAO,cAAe,CACtB,EAAa,aAAa,EAAa,UAAY,qBAAqB,EAExE,IAAM,EAAiB,KAAK,sBAAsB,EAClD,EACI,yBAAyB,EAAe,SAAW,EAAI,SAAW,EAAe,KAAK,IAAI,GAC9F,EACA,GAAI,CACA,EAAQ,EAAW,CACf,GAAG,KAAK,2BACR,KAAM,EACN,WAAY,EACZ,SAAU,EAAO,SAAS,KAAK,CAAM,CACzC,CAAC,CACL,MAAQ,CAAC,CACb,CACA,EAAa,iBAAmB,EAChC,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,CAAW,EAAG,EAAY,IAAI,EAAK,CAAK,EAClF,EAAa,oBAAoB,EAAY,KAAK,YAAY,EAC1D,EAAa,iBAAiB,KAAK,mBAAmB,CAC9D,CAEA,OAAO,CACX,CAOA,OAAO,MAAa,CAChB,KAAU,MACd,CAOA,OAAO,UAAU,EAAsB,CACnC,EAAa,QAAU,EACvB,EAAsB,CAAM,EAC5B,KAAK,aAAa,CACtB,CAKA,OAAO,EAAsC,CACzC,OAAO,EAAa,gBAAgB,CAAG,CAAC,CAAC,KAC7C,CACJ"}
|
package/dist/browser/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Converters as e,isArrayOf as t}from"./converters/Converters.mjs";import{EnvaptError as n,EnvaptErrorCodes as r}from"./infra/Error.mjs";import{Environment as i}from"./core/EnvironmentMethods.mjs";import{
|
|
1
|
+
import{Converters as e,isArrayOf as t}from"./converters/Converters.mjs";import{EnvaptError as n,EnvaptErrorCodes as r}from"./infra/Error.mjs";import{Environment as i}from"./core/EnvironmentMethods.mjs";import{PortableSource as a}from"./sources/PortableSource.mjs";import{ManualEnvSource as o}from"./sources/ManualEnvSource.mjs";import{WorkerEnvSource as s}from"./sources/WorkerEnvSource.mjs";import{Envapt as c}from"./decorators/modern/Envapt.mjs";import{EnvBool as l,EnvNum as u,EnvStr as d,EnvTime as f,EnvUrl as p}from"./decorators/modern/SugarDecorators.mjs";import{PortableEnvapter as m}from"./engine/PortableEnvapter.mjs";export{e as Converters,l as EnvBool,u as EnvNum,d as EnvStr,f as EnvTime,p as EnvUrl,c as Envapt,n as EnvaptError,r as EnvaptErrorCodes,m as Envapter,i as Environment,o as ManualEnvSource,a as PortableSource,s as WorkerEnvSource,t as isArrayOf};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.mjs","names":[],"sources":["../../../src/infra/runtime.ts"],"sourcesContent":["import type {
|
|
1
|
+
{"version":3,"file":"runtime.mjs","names":[],"sources":["../../../src/infra/runtime.ts"],"sourcesContent":["import type { Source } from '../types';\n\n// importing Debug back into EnvapterBase would cycle, so the source and sink are injected via the setters below.\n/* v8 ignore start -- @preserve replaced at load on Node, so the Node suite never runs these defaults */\nlet sink: (line: string) => void = (line) => {\n // eslint-disable-next-line no-console -- the off-Node fallback log sink\n console.error(line);\n};\nlet envReader: () => Record<string, string> = () => ({});\n/* v8 ignore stop */\n\nexport function setRuntimeSink(fn: (line: string) => void): void {\n sink = fn;\n}\n\nexport function writeRuntimeLine(line: string): void {\n sink(line);\n}\n\nexport function readRuntimeEnv(): Record<string, string> {\n return envReader();\n}\n\nexport function bindRuntimeFromSource(source: Source): void {\n envReader = (): Record<string, string> => source.readVars();\n}\n"],"mappings":"AAIA,IAAI,EAAgC,GAAS,CAEzC,QAAQ,MAAM,CAAI,CACtB,EACI,OAAiD,CAAC,GAOtD,SAAgB,EAAiB,EAAoB,CACjD,EAAK,CAAI,CACb,CAEA,SAAgB,GAAyC,CACrD,OAAO,EAAU,CACrB,CAEA,SAAgB,EAAsB,EAAsB,CACxD,MAA0C,EAAO,SAAS,CAC9D"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{PortableSource as e}from"./PortableSource.mjs";var t=class extends e{};export{t as ManualEnvSource};
|
|
2
2
|
//# sourceMappingURL=ManualEnvSource.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ManualEnvSource.mjs","names":[],"sources":["../../../src/sources/ManualEnvSource.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"ManualEnvSource.mjs","names":[],"sources":["../../../src/sources/ManualEnvSource.ts"],"sourcesContent":["import { PortableSource } from './PortableSource';\n\n/**\n * @deprecated Renamed to {@link PortableSource}, which serves every no-filesystem runtime the same way.\n * Removed in v8. Replace `new ManualEnvSource(obj)` with `new PortableSource(obj)`.\n * @public\n */\nexport class ManualEnvSource extends PortableSource {}\n"],"mappings":"sDAOA,IAAa,EAAb,cAAqC,CAAe,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PortableSource.mjs","names":[],"sources":["../../../src/sources/PortableSource.ts"],"sourcesContent":["import { coerceToStringRecord } from './coerce';\n\nimport type { BareEnvSource } from '../types';\n\n/**\n * Environment source seeded from an object you provide, for every runtime without a filesystem. The\n * config your bundler injects into a browser build (Vite's `import.meta.env`, a webpack `DefinePlugin`\n * global), the Cloudflare `env` binding, a Vercel Edge or Fastly config object, a test fixture, or any\n * plain object. Pass it straight through. Non-string values are JSON-stringified so the converters\n * still apply, which means they must be JSON-serializable. Without a filesystem the `.env` cascade and\n * file APIs do not apply.\n * @public\n */\nexport class PortableSource implements BareEnvSource {\n /** Always `false`. With no filesystem, the `.env` cascade and file APIs do not apply. */\n readonly supportsFiles = false;\n private readonly vars: Record<string, string>;\n\n /** Seed the source from `vars`. Non-string values are JSON-stringified so the converters still apply. */\n constructor(vars: object) {\n this.vars = coerceToStringRecord(vars);\n }\n\n /** Returns a snapshot clone of the provided object as plain strings. */\n readVars(): Record<string, string> {\n return { ...this.vars };\n }\n}\n"],"mappings":"oDAaA,IAAa,EAAb,KAAqD,CAEjD,cAAyB,GACzB,KAGA,YAAY,EAAc,CACtB,KAAK,KAAO,EAAqB,CAAI,CACzC,CAGA,UAAmC,CAC/B,MAAO,CAAE,GAAG,KAAK,IAAK,CAC1B,CACJ"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{EnvaptError as e}from"../infra/Error.mjs";var t=class{supportsFiles=!1;readVars(){throw new e(307,`No environment source is bound. Call Envapter.useSource(...) with a source such as new
|
|
1
|
+
import{EnvaptError as e}from"../infra/Error.mjs";var t=class{supportsFiles=!1;readVars(){throw new e(307,`No environment source is bound. Call Envapter.useSource(...) with a source such as new PortableSource({...}) before reading.`)}};export{t as UnboundEnvSource};
|
|
2
2
|
//# sourceMappingURL=UnboundEnvSource.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UnboundEnvSource.mjs","names":[],"sources":["../../../src/sources/UnboundEnvSource.ts"],"sourcesContent":["import { EnvaptError, EnvaptErrorCodes } from '../infra/Error';\n\nimport type { BareEnvSource } from '../types';\n\n/**\n * The default source until one is bound. Every read throws NoSourceBound so a forgotten `useSource`\n * surfaces at read time instead of silently yielding nothing. The Node entry binds {@link NodeEnvSource}\n * at load
|
|
1
|
+
{"version":3,"file":"UnboundEnvSource.mjs","names":[],"sources":["../../../src/sources/UnboundEnvSource.ts"],"sourcesContent":["import { EnvaptError, EnvaptErrorCodes } from '../infra/Error';\n\nimport type { BareEnvSource } from '../types';\n\n/**\n * The default source until one is bound. Every read throws NoSourceBound so a forgotten `useSource`\n * surfaces at read time instead of silently yielding nothing. The Node entry binds {@link NodeEnvSource}\n * at load, browser and Workers bundles must call `Envapter.useSource(...)` before reading.\n * @internal\n */\nexport class UnboundEnvSource implements BareEnvSource {\n readonly supportsFiles = false;\n\n readVars(): Record<string, string> {\n throw new EnvaptError(\n EnvaptErrorCodes.NoSourceBound,\n 'No environment source is bound. Call Envapter.useSource(...) with a source such as new PortableSource({...}) before reading.'\n );\n }\n}\n"],"mappings":"iDAUA,IAAa,EAAb,KAAuD,CACnD,cAAyB,GAEzB,UAAmC,CAC/B,MAAM,IAAI,EAAA,IAEN,8HACJ,CACJ,CACJ"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{PortableSource as e}from"./PortableSource.mjs";var t=class extends e{};export{t as WorkerEnvSource};
|
|
2
2
|
//# sourceMappingURL=WorkerEnvSource.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkerEnvSource.mjs","names":[],"sources":["../../../src/sources/WorkerEnvSource.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"WorkerEnvSource.mjs","names":[],"sources":["../../../src/sources/WorkerEnvSource.ts"],"sourcesContent":["import { PortableSource } from './PortableSource';\n\n/**\n * @deprecated Use {@link PortableSource} instead, which wraps the Cloudflare `env` binding identically.\n * Removed in v8. Replace `new WorkerEnvSource(env)` with `new PortableSource(env)`.\n * @public\n */\nexport class WorkerEnvSource extends PortableSource {}\n"],"mappings":"sDAOA,IAAa,EAAb,cAAqC,CAAe,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coerce.mjs","names":[],"sources":["../../../src/sources/coerce.ts"],"sourcesContent":["// Shared by
|
|
1
|
+
{"version":3,"file":"coerce.mjs","names":[],"sources":["../../../src/sources/coerce.ts"],"sourcesContent":["// Shared by PortableSource and its deprecated aliases. The returned record is fresh, so a caller\n// mutating its object later cannot leak into the source, and non-string values are JSON-stringified.\nexport function coerceToStringRecord(env: object): Record<string, string> {\n const snapshot: Record<string, string> = {};\n // `object` so a Cloudflare `Env` (an interface with no index signature) is accepted. cast to a record\n // to read entries as `unknown` rather than `any`.\n for (const [key, value] of Object.entries(env as Record<string, unknown>)) {\n if (typeof value === 'string') {\n snapshot[key] = value;\n continue;\n }\n const encoded = JSON.stringify(value);\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- TS lib mistypes JSON.stringify's return as always-string; it is string | undefined at runtime\n if (encoded !== undefined) snapshot[key] = encoded;\n }\n return snapshot;\n}\n"],"mappings":"AAEA,SAAgB,EAAqB,EAAqC,CACtE,IAAM,EAAmC,CAAC,EAG1C,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,CAA8B,EAAG,CACvE,GAAI,OAAO,GAAU,SAAU,CAC3B,EAAS,GAAO,EAChB,QACJ,CACA,IAAM,EAAU,KAAK,UAAU,CAAK,EAEhC,IAAY,IAAA,KAAW,EAAS,GAAO,EAC/C,CACA,OAAO,CACX"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EnvapterBase.cjs","names":["UnboundEnvSource","getDebugLevel","EnvaptError","loadDotenv"],"sources":["../../../src/core/EnvapterBase.ts"],"sourcesContent":["import { Validator } from '../engine/Validators';\nimport { debugVerbose, getDebugLevel, setDebugLevel } from '../infra/Debug';\nimport { loadDotenv } from '../infra/Dotenv';\nimport { EnvaptError, EnvaptErrorCodes } from '../infra/Error';\nimport { bindRuntimeFromSource } from '../infra/runtime';\nimport { UnboundEnvSource } from '../sources/UnboundEnvSource';\n\nimport type { DebugLevel } from '../infra/Debug';\nimport type { EnvFileOptions } from '../infra/Dotenv';\nimport type { EnvKeyInput, EnvSource, FileEnvSource } from '../types';\n\n/** @internal */\nexport const EnvaptCache = new Map<string, unknown>();\n\n/** @internal */\nexport abstract class EnvapterBase {\n protected static _envPaths: string[] = ['.env'];\n protected static _envPathsExplicitlySet = false;\n protected static _baseDir: string | undefined = undefined;\n protected static _userDefinedEnvFileOptions: EnvFileOptions = {};\n protected static _strict = false;\n protected static _syncProcessEnv = false;\n // Loader-written keys only (collisions skipped). Refilled on every cache rebuild.\n protected static _dotenvAddedKeys: Set<string> = new Set<string>();\n // Unbound by default so non-Node builds throw NoSourceBound on read until useSource() is called.\n // NodeEnvapter's static block binds NodeEnvSource when referenced, so `import 'envapt'` needs no setup.\n protected static _source: EnvSource = new UnboundEnvSource();\n\n /**\n * Enable or disable strict mode. Default `false`. Setting refreshes the cache so\n * previously-cached converted values get re-evaluated under the new rule.\n */\n static set strict(value: boolean) {\n // Anchored to EnvapterBase: `this._strict` would write an own-property on the subclass that base readers miss.\n EnvapterBase._strict = value;\n // `this`, not EnvapterBase: rebuild via the subclass so its `resolveEffectivePaths` override is honored.\n this.refreshCache();\n }\n\n static get strict(): boolean {\n return EnvapterBase._strict;\n }\n\n /**\n * Set the debug log level. Defaults to `silent`. When unset, reads `ENVAPT_DEBUG` from the\n * bound source on first access; the setter overrides any env-var value. Output goes to stderr\n * on Node (the console elsewhere), prefixed with `[envapt]`.\n */\n static set debug(level: DebugLevel) {\n setDebugLevel(level);\n }\n\n static get debug(): DebugLevel {\n return getDebugLevel();\n }\n\n /**\n * Opt-in mirror of dotenv-loaded keys back to `process.env`. Default `false`.\n *\n * Only keys the loader actually wrote are mirrored, so collision behavior follows\n * `envFileOptions.override`: with the default `false`, pre-existing `process.env` values\n * are preserved; with `true`, the file value wins in both the cache and the mirror.\n *\n * Flipping `false → true` mirrors the existing tracked delta immediately (no cache\n * refresh). Flipping `true → false` is one-way: previously mirrored keys remain in\n * `process.env` until the process exits.\n */\n static set syncProcessEnv(value: boolean) {\n Validator.validateSyncProcessEnv(value);\n const previous = EnvapterBase._syncProcessEnv;\n // Anchored to EnvapterBase: `this._syncProcessEnv` would write an own-property on the subclass that base readers miss.\n EnvapterBase._syncProcessEnv = value;\n if (!previous && value && EnvaptCache.size > 0) this.mirrorToProcessEnv();\n }\n\n static get syncProcessEnv(): boolean {\n return EnvapterBase._syncProcessEnv;\n }\n\n protected static treatAsMissing(value: string | undefined): boolean {\n if (value === undefined || value === '') return true;\n if (EnvapterBase._strict && value.trim() === '') return true;\n return false;\n }\n\n // No baseDir: candidate returned unchanged so the source resolves it against its own default\n // (process.cwd() on Node). Resolution goes through the source to keep this class node-free.\n protected static resolveAgainstBase(candidate: string): string {\n const baseDir = EnvapterBase._baseDir;\n if (baseDir === undefined) return candidate;\n const source = EnvapterBase._source;\n /* v8 ignore next -- @preserve callers are all file-gated, so the source is never bare here */\n if (!source.supportsFiles) return candidate;\n return source.resolvePath(baseDir, candidate);\n }\n\n // File-based config (envPaths/baseDir/configureProfiles) is meaningless without a filesystem;\n // throw instead of silently ignoring it on the browser or Workers. Narrows the source so callers\n // can reach the file capabilities (resolvePath/normalizeBaseDir) after the check.\n protected static assertFileApiSupported(api: string, source: EnvSource): asserts source is FileEnvSource {\n if (!source.supportsFiles) {\n throw new EnvaptError(\n EnvaptErrorCodes.FileApiUnsupported,\n `${api} requires a filesystem-backed source; the bound source does not support .env files.`\n );\n }\n }\n\n // Existence via the bound source instead of fs.existsSync/accessSync: a file \"exists\" when the\n // source can read it.\n protected static sourceFileExists(path: string): boolean {\n const source = EnvapterBase._source;\n /* v8 ignore next -- @preserve every caller is file-gated, so this never sees a bare source */\n if (!source.supportsFiles) return false;\n return source.readFile(path, 'utf8') !== undefined;\n }\n\n protected static refreshCache(): void {\n EnvaptCache.clear();\n EnvapterBase._dotenvAddedKeys = new Set();\n debugVerbose('cache cleared, reloading config');\n void this.config; // getter rebuilds the cache as a side effect\n }\n\n protected static mirrorToProcessEnv(): void {\n if (EnvapterBase._dotenvAddedKeys.size === 0) return;\n const source = EnvapterBase._source;\n /* v8 ignore next -- @preserve dotenv keys only accumulate under a file source, so the delta implies supportsFiles here */\n if (!source.supportsFiles) return;\n const mirrored: Record<string, string> = {};\n for (const key of EnvapterBase._dotenvAddedKeys) {\n const value = EnvaptCache.get(key);\n /* v8 ignore next -- @preserve loader only writes strings; defensive against future cache contents */\n if (typeof value !== 'string') continue;\n mirrored[key] = this.resolveForMirror(key, value);\n debugVerbose(`mirrored ${key} to the ambient environment`);\n }\n source.writeVars(mirrored);\n debugVerbose(`mirrored ${EnvapterBase._dotenvAddedKeys.size} keys to the ambient environment`);\n }\n\n // The template resolver is defined in PrimitiveMethods, and EnvapterBase can't call it without an\n // import cycle, so the mirror expands ${VAR} through this override.\n protected static resolveForMirror(_key: string, value: string): string {\n /* v8 ignore next -- @preserve overridden by PrimitiveMethods on every concrete class */\n return value;\n }\n\n // Default returns the explicit `_envPaths`; EnvironmentMethods overrides to layer the dotenv-flow\n // cascade + configureProfiles when envPaths was never explicitly set.\n protected static resolveEffectivePaths(): string[] {\n /* v8 ignore next -- @preserve */\n return this._envPaths.map((p) => this.resolveAgainstBase(p));\n }\n\n protected static resolveKeyInput(keyInput: EnvKeyInput): { key: string; value: string | undefined } {\n const keys = Array.isArray(keyInput) ? keyInput : [keyInput];\n const normalizedKeys = keys as readonly string[];\n\n if (normalizedKeys.length === 0) {\n throw new EnvaptError(EnvaptErrorCodes.InvalidKeyInput, 'At least one environment key must be provided.');\n }\n\n if (normalizedKeys.some((k) => typeof k !== 'string')) {\n throw new EnvaptError(EnvaptErrorCodes.InvalidKeyInput, 'Environment keys must be strings.');\n }\n\n if (normalizedKeys.some((k) => k.trim() === '')) {\n throw new EnvaptError(EnvaptErrorCodes.InvalidKeyInput, 'Environment keys cannot be empty strings.');\n }\n\n for (const candidate of normalizedKeys) {\n const value = this.config.get(candidate) as string | undefined;\n if (value !== undefined) {\n return { key: candidate, value };\n }\n }\n\n return { key: normalizedKeys[0] as string, value: undefined };\n }\n\n protected static get config(): Map<string, unknown> {\n if (EnvaptCache.size === 0) {\n const source = EnvapterBase._source;\n // Clone so the loader and downstream reads never mutate the source's backing object.\n const isolatedEnv: Record<string, string> = { ...source.readVars() };\n\n let added = new Set<string>();\n // Sources without a filesystem (injected objects on the browser or Workers) skip the\n // .env cascade, profiles, and envPaths; only the readVars() snapshot populates the cache.\n if (source.supportsFiles) {\n debugVerbose(`base dir: ${EnvapterBase._baseDir ?? 'working directory'}`);\n // Outside the try below so a missing configured profile path surfaces its EnvaptError; only dotenv parse errors stay caught.\n const effectivePaths = this.resolveEffectivePaths();\n debugVerbose(\n `effective .env paths: ${effectivePaths.length === 0 ? '(none)' : effectivePaths.join(', ')}`\n );\n try {\n added = loadDotenv({\n ...this._userDefinedEnvFileOptions,\n path: effectivePaths,\n processEnv: isolatedEnv,\n readFile: source.readFile.bind(source)\n });\n } catch {}\n }\n EnvapterBase._dotenvAddedKeys = added;\n for (const [key, value] of Object.entries(isolatedEnv)) EnvaptCache.set(key, value);\n debugVerbose(`cache populated: ${EnvaptCache.size} keys total`);\n if (EnvapterBase._syncProcessEnv) this.mirrorToProcessEnv();\n }\n\n return EnvaptCache;\n }\n\n /**\n * Eagerly load the `.env` cascade now instead of lazily on the first read. Idempotent: a no-op\n * once the cache is built. Useful before mirroring to `process.env` (see {@link syncProcessEnv}),\n * which is what the `envapt/config` side-effect entry does.\n */\n static load(): void {\n void this.config;\n }\n\n /**\n * Bind the environment {@link EnvSource}. On Node the entry binds {@link NodeEnvSource} for you\n * (a `process.env` snapshot plus the `.env` cascade); on the browser or Workers, pass a\n * `ManualEnvSource` / `WorkerEnvSource` (or any `EnvSource`) before reading. Clears and rebuilds\n * the cache.\n */\n static useSource(source: EnvSource): void {\n EnvapterBase._source = source;\n bindRuntimeFromSource(source);\n this.refreshCache();\n }\n\n /**\n * Read an environment variable as its raw string, skipping parsing and conversion.\n */\n getRaw(key: EnvKeyInput): string | undefined {\n return EnvapterBase.resolveKeyInput(key).value;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EnvapterBase.cjs","names":["UnboundEnvSource","getDebugLevel","EnvaptError","loadDotenv"],"sources":["../../../src/core/EnvapterBase.ts"],"sourcesContent":["import { Validator } from '../engine/Validators';\nimport { debugVerbose, getDebugLevel, setDebugLevel } from '../infra/Debug';\nimport { loadDotenv } from '../infra/Dotenv';\nimport { EnvaptError, EnvaptErrorCodes } from '../infra/Error';\nimport { bindRuntimeFromSource } from '../infra/runtime';\nimport { UnboundEnvSource } from '../sources/UnboundEnvSource';\n\nimport type { DebugLevel } from '../infra/Debug';\nimport type { EnvFileOptions } from '../infra/Dotenv';\nimport type { EnvKeyInput, FileEnvSource, Source } from '../types';\n\n/** @internal */\nexport const EnvaptCache = new Map<string, unknown>();\n\n/** @internal */\nexport abstract class EnvapterBase {\n protected static _envPaths: string[] = ['.env'];\n protected static _envPathsExplicitlySet = false;\n protected static _baseDir: string | undefined = undefined;\n protected static _userDefinedEnvFileOptions: EnvFileOptions = {};\n protected static _strict = false;\n protected static _syncProcessEnv = false;\n // Loader-written keys only (collisions skipped). Refilled on every cache rebuild.\n protected static _dotenvAddedKeys: Set<string> = new Set<string>();\n // Unbound by default so non-Node builds throw NoSourceBound on read until useSource() is called.\n // NodeEnvapter's static block binds NodeEnvSource when referenced, so `import 'envapt'` needs no setup.\n protected static _source: Source = new UnboundEnvSource();\n\n /**\n * Enable or disable strict mode. Default `false`. Setting refreshes the cache so\n * previously-cached converted values get re-evaluated under the new rule.\n */\n static set strict(value: boolean) {\n // Anchored to EnvapterBase: `this._strict` would write an own-property on the subclass that base readers miss.\n EnvapterBase._strict = value;\n // rebuild via `this` so the subclass `resolveEffectivePaths` override is honored (EnvapterBase would skip it).\n this.refreshCache();\n }\n\n static get strict(): boolean {\n return EnvapterBase._strict;\n }\n\n /**\n * Set the debug log level. Defaults to `silent`. When unset, reads `ENVAPT_DEBUG` from the\n * bound source on first access. The setter overrides any env-var value. Output goes to stderr\n * on Node (the console elsewhere), prefixed with `[envapt]`.\n */\n static set debug(level: DebugLevel) {\n setDebugLevel(level);\n }\n\n static get debug(): DebugLevel {\n return getDebugLevel();\n }\n\n /**\n * Opt-in mirror of dotenv-loaded keys back to `process.env`. Default `false`.\n *\n * Only keys the loader actually wrote are mirrored, so collision behavior follows\n * `envFileOptions.override`: with the default `false`, pre-existing `process.env` values\n * are preserved. With `true`, the file value wins in both the cache and the mirror.\n *\n * Flipping `false → true` mirrors the existing tracked delta immediately (no cache\n * refresh). Flipping `true → false` is one-way: previously mirrored keys remain in\n * `process.env` until the process exits.\n */\n static set syncProcessEnv(value: boolean) {\n Validator.validateSyncProcessEnv(value);\n const previous = EnvapterBase._syncProcessEnv;\n // Anchored to EnvapterBase: `this._syncProcessEnv` would write an own-property on the subclass that base readers miss.\n EnvapterBase._syncProcessEnv = value;\n if (!previous && value && EnvaptCache.size > 0) this.mirrorToProcessEnv();\n }\n\n static get syncProcessEnv(): boolean {\n return EnvapterBase._syncProcessEnv;\n }\n\n protected static treatAsMissing(value: string | undefined): boolean {\n if (value === undefined || value === '') return true;\n if (EnvapterBase._strict && value.trim() === '') return true;\n return false;\n }\n\n // No baseDir: candidate returned unchanged so the source resolves it against its own default\n // (process.cwd() on Node). Resolution goes through the source to keep this class node-free.\n protected static resolveAgainstBase(candidate: string): string {\n const baseDir = EnvapterBase._baseDir;\n if (baseDir === undefined) return candidate;\n const source = EnvapterBase._source;\n /* v8 ignore next -- @preserve callers are all file-gated, so the source is never bare here */\n if (!source.supportsFiles) return candidate;\n return source.resolvePath(baseDir, candidate);\n }\n\n // File-based config (envPaths/baseDir/configureProfiles) is meaningless without a filesystem, and\n // it throws instead of silently ignoring it on the browser or Workers. Narrows the source so callers\n // can reach the file capabilities (resolvePath/normalizeBaseDir) after the check.\n protected static assertFileApiSupported(api: string, source: Source): asserts source is FileEnvSource {\n if (!source.supportsFiles) {\n throw new EnvaptError(\n EnvaptErrorCodes.FileApiUnsupported,\n `${api} requires a filesystem-backed source; the bound source does not support .env files.`\n );\n }\n }\n\n // Existence via the bound source instead of fs.existsSync/accessSync: a file \"exists\" when the\n // source can read it.\n protected static sourceFileExists(path: string): boolean {\n const source = EnvapterBase._source;\n /* v8 ignore next -- @preserve every caller is file-gated, so this never sees a bare source */\n if (!source.supportsFiles) return false;\n return source.readFile(path, 'utf8') !== undefined;\n }\n\n protected static refreshCache(): void {\n EnvaptCache.clear();\n EnvapterBase._dotenvAddedKeys = new Set();\n debugVerbose('cache cleared, reloading config');\n void this.config; // getter rebuilds the cache as a side effect\n }\n\n protected static mirrorToProcessEnv(): void {\n if (EnvapterBase._dotenvAddedKeys.size === 0) return;\n const source = EnvapterBase._source;\n /* v8 ignore next -- @preserve dotenv keys only accumulate under a file source, so the delta implies supportsFiles here */\n if (!source.supportsFiles) return;\n const mirrored: Record<string, string> = {};\n for (const key of EnvapterBase._dotenvAddedKeys) {\n const value = EnvaptCache.get(key);\n /* v8 ignore next -- @preserve loader only writes strings, defensive against future cache contents */\n if (typeof value !== 'string') continue;\n mirrored[key] = this.resolveForMirror(key, value);\n debugVerbose(`mirrored ${key} to the ambient environment`);\n }\n source.writeVars(mirrored);\n debugVerbose(`mirrored ${EnvapterBase._dotenvAddedKeys.size} keys to the ambient environment`);\n }\n\n // The template resolver is defined in PrimitiveMethods, and EnvapterBase can't call it without an\n // import cycle, so the mirror expands ${VAR} through this override.\n protected static resolveForMirror(_key: string, value: string): string {\n /* v8 ignore next -- @preserve overridden by PrimitiveMethods on every concrete class */\n return value;\n }\n\n // Default returns the explicit `_envPaths`. EnvironmentMethods overrides to layer the dotenv-flow\n // cascade + configureProfiles when envPaths was never explicitly set.\n protected static resolveEffectivePaths(): string[] {\n /* v8 ignore next -- @preserve */\n return this._envPaths.map((p) => this.resolveAgainstBase(p));\n }\n\n protected static resolveKeyInput(keyInput: EnvKeyInput): { key: string; value: string | undefined } {\n const keys = Array.isArray(keyInput) ? keyInput : [keyInput];\n const normalizedKeys = keys as readonly string[];\n\n if (normalizedKeys.length === 0) {\n throw new EnvaptError(EnvaptErrorCodes.InvalidKeyInput, 'At least one environment key must be provided.');\n }\n\n if (normalizedKeys.some((k) => typeof k !== 'string')) {\n throw new EnvaptError(EnvaptErrorCodes.InvalidKeyInput, 'Environment keys must be strings.');\n }\n\n if (normalizedKeys.some((k) => k.trim() === '')) {\n throw new EnvaptError(EnvaptErrorCodes.InvalidKeyInput, 'Environment keys cannot be empty strings.');\n }\n\n for (const candidate of normalizedKeys) {\n const value = this.config.get(candidate) as string | undefined;\n if (value !== undefined) {\n return { key: candidate, value };\n }\n }\n\n return { key: normalizedKeys[0] as string, value: undefined };\n }\n\n protected static get config(): Map<string, unknown> {\n if (EnvaptCache.size === 0) {\n const source = EnvapterBase._source;\n // Clone so the loader and downstream reads never mutate the source's backing object.\n const isolatedEnv: Record<string, string> = { ...source.readVars() };\n\n let added = new Set<string>();\n // Sources without a filesystem (injected objects on the browser or Workers) skip the\n // .env cascade, profiles, and envPaths. Only the readVars() snapshot populates the cache.\n if (source.supportsFiles) {\n debugVerbose(`base dir: ${EnvapterBase._baseDir ?? 'working directory'}`);\n // Outside the try below so a missing configured profile path surfaces its EnvaptError. Only dotenv parse errors stay caught.\n const effectivePaths = this.resolveEffectivePaths();\n debugVerbose(\n `effective .env paths: ${effectivePaths.length === 0 ? '(none)' : effectivePaths.join(', ')}`\n );\n try {\n added = loadDotenv({\n ...this._userDefinedEnvFileOptions,\n path: effectivePaths,\n processEnv: isolatedEnv,\n readFile: source.readFile.bind(source)\n });\n } catch {}\n }\n EnvapterBase._dotenvAddedKeys = added;\n for (const [key, value] of Object.entries(isolatedEnv)) EnvaptCache.set(key, value);\n debugVerbose(`cache populated: ${EnvaptCache.size} keys total`);\n if (EnvapterBase._syncProcessEnv) this.mirrorToProcessEnv();\n }\n\n return EnvaptCache;\n }\n\n /**\n * Eagerly load the `.env` cascade now instead of lazily on the first read. Idempotent: a no-op\n * once the cache is built. Useful before mirroring to `process.env` (see {@link syncProcessEnv}),\n * which is what the `envapt/config` side-effect entry does.\n */\n static load(): void {\n void this.config;\n }\n\n /**\n * Bind the environment {@link Source}. On Node the entry binds {@link NodeEnvSource} for you\n * (a `process.env` snapshot plus the `.env` cascade). On the browser or Workers, pass a\n * `PortableSource` (or any `Source`) before reading. Clears and rebuilds the cache.\n */\n static useSource(source: Source): void {\n EnvapterBase._source = source;\n bindRuntimeFromSource(source);\n this.refreshCache();\n }\n\n /**\n * Read an environment variable as its raw string, skipping parsing and conversion.\n */\n getRaw(key: EnvKeyInput): string | undefined {\n return EnvapterBase.resolveKeyInput(key).value;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EnvapterBase.mjs","names":[],"sources":["../../../src/core/EnvapterBase.ts"],"sourcesContent":["import { Validator } from '../engine/Validators';\nimport { debugVerbose, getDebugLevel, setDebugLevel } from '../infra/Debug';\nimport { loadDotenv } from '../infra/Dotenv';\nimport { EnvaptError, EnvaptErrorCodes } from '../infra/Error';\nimport { bindRuntimeFromSource } from '../infra/runtime';\nimport { UnboundEnvSource } from '../sources/UnboundEnvSource';\n\nimport type { DebugLevel } from '../infra/Debug';\nimport type { EnvFileOptions } from '../infra/Dotenv';\nimport type { EnvKeyInput, EnvSource, FileEnvSource } from '../types';\n\n/** @internal */\nexport const EnvaptCache = new Map<string, unknown>();\n\n/** @internal */\nexport abstract class EnvapterBase {\n protected static _envPaths: string[] = ['.env'];\n protected static _envPathsExplicitlySet = false;\n protected static _baseDir: string | undefined = undefined;\n protected static _userDefinedEnvFileOptions: EnvFileOptions = {};\n protected static _strict = false;\n protected static _syncProcessEnv = false;\n // Loader-written keys only (collisions skipped). Refilled on every cache rebuild.\n protected static _dotenvAddedKeys: Set<string> = new Set<string>();\n // Unbound by default so non-Node builds throw NoSourceBound on read until useSource() is called.\n // NodeEnvapter's static block binds NodeEnvSource when referenced, so `import 'envapt'` needs no setup.\n protected static _source: EnvSource = new UnboundEnvSource();\n\n /**\n * Enable or disable strict mode. Default `false`. Setting refreshes the cache so\n * previously-cached converted values get re-evaluated under the new rule.\n */\n static set strict(value: boolean) {\n // Anchored to EnvapterBase: `this._strict` would write an own-property on the subclass that base readers miss.\n EnvapterBase._strict = value;\n // `this`, not EnvapterBase: rebuild via the subclass so its `resolveEffectivePaths` override is honored.\n this.refreshCache();\n }\n\n static get strict(): boolean {\n return EnvapterBase._strict;\n }\n\n /**\n * Set the debug log level. Defaults to `silent`. When unset, reads `ENVAPT_DEBUG` from the\n * bound source on first access; the setter overrides any env-var value. Output goes to stderr\n * on Node (the console elsewhere), prefixed with `[envapt]`.\n */\n static set debug(level: DebugLevel) {\n setDebugLevel(level);\n }\n\n static get debug(): DebugLevel {\n return getDebugLevel();\n }\n\n /**\n * Opt-in mirror of dotenv-loaded keys back to `process.env`. Default `false`.\n *\n * Only keys the loader actually wrote are mirrored, so collision behavior follows\n * `envFileOptions.override`: with the default `false`, pre-existing `process.env` values\n * are preserved; with `true`, the file value wins in both the cache and the mirror.\n *\n * Flipping `false → true` mirrors the existing tracked delta immediately (no cache\n * refresh). Flipping `true → false` is one-way: previously mirrored keys remain in\n * `process.env` until the process exits.\n */\n static set syncProcessEnv(value: boolean) {\n Validator.validateSyncProcessEnv(value);\n const previous = EnvapterBase._syncProcessEnv;\n // Anchored to EnvapterBase: `this._syncProcessEnv` would write an own-property on the subclass that base readers miss.\n EnvapterBase._syncProcessEnv = value;\n if (!previous && value && EnvaptCache.size > 0) this.mirrorToProcessEnv();\n }\n\n static get syncProcessEnv(): boolean {\n return EnvapterBase._syncProcessEnv;\n }\n\n protected static treatAsMissing(value: string | undefined): boolean {\n if (value === undefined || value === '') return true;\n if (EnvapterBase._strict && value.trim() === '') return true;\n return false;\n }\n\n // No baseDir: candidate returned unchanged so the source resolves it against its own default\n // (process.cwd() on Node). Resolution goes through the source to keep this class node-free.\n protected static resolveAgainstBase(candidate: string): string {\n const baseDir = EnvapterBase._baseDir;\n if (baseDir === undefined) return candidate;\n const source = EnvapterBase._source;\n /* v8 ignore next -- @preserve callers are all file-gated, so the source is never bare here */\n if (!source.supportsFiles) return candidate;\n return source.resolvePath(baseDir, candidate);\n }\n\n // File-based config (envPaths/baseDir/configureProfiles) is meaningless without a filesystem;\n // throw instead of silently ignoring it on the browser or Workers. Narrows the source so callers\n // can reach the file capabilities (resolvePath/normalizeBaseDir) after the check.\n protected static assertFileApiSupported(api: string, source: EnvSource): asserts source is FileEnvSource {\n if (!source.supportsFiles) {\n throw new EnvaptError(\n EnvaptErrorCodes.FileApiUnsupported,\n `${api} requires a filesystem-backed source; the bound source does not support .env files.`\n );\n }\n }\n\n // Existence via the bound source instead of fs.existsSync/accessSync: a file \"exists\" when the\n // source can read it.\n protected static sourceFileExists(path: string): boolean {\n const source = EnvapterBase._source;\n /* v8 ignore next -- @preserve every caller is file-gated, so this never sees a bare source */\n if (!source.supportsFiles) return false;\n return source.readFile(path, 'utf8') !== undefined;\n }\n\n protected static refreshCache(): void {\n EnvaptCache.clear();\n EnvapterBase._dotenvAddedKeys = new Set();\n debugVerbose('cache cleared, reloading config');\n void this.config; // getter rebuilds the cache as a side effect\n }\n\n protected static mirrorToProcessEnv(): void {\n if (EnvapterBase._dotenvAddedKeys.size === 0) return;\n const source = EnvapterBase._source;\n /* v8 ignore next -- @preserve dotenv keys only accumulate under a file source, so the delta implies supportsFiles here */\n if (!source.supportsFiles) return;\n const mirrored: Record<string, string> = {};\n for (const key of EnvapterBase._dotenvAddedKeys) {\n const value = EnvaptCache.get(key);\n /* v8 ignore next -- @preserve loader only writes strings; defensive against future cache contents */\n if (typeof value !== 'string') continue;\n mirrored[key] = this.resolveForMirror(key, value);\n debugVerbose(`mirrored ${key} to the ambient environment`);\n }\n source.writeVars(mirrored);\n debugVerbose(`mirrored ${EnvapterBase._dotenvAddedKeys.size} keys to the ambient environment`);\n }\n\n // The template resolver is defined in PrimitiveMethods, and EnvapterBase can't call it without an\n // import cycle, so the mirror expands ${VAR} through this override.\n protected static resolveForMirror(_key: string, value: string): string {\n /* v8 ignore next -- @preserve overridden by PrimitiveMethods on every concrete class */\n return value;\n }\n\n // Default returns the explicit `_envPaths`; EnvironmentMethods overrides to layer the dotenv-flow\n // cascade + configureProfiles when envPaths was never explicitly set.\n protected static resolveEffectivePaths(): string[] {\n /* v8 ignore next -- @preserve */\n return this._envPaths.map((p) => this.resolveAgainstBase(p));\n }\n\n protected static resolveKeyInput(keyInput: EnvKeyInput): { key: string; value: string | undefined } {\n const keys = Array.isArray(keyInput) ? keyInput : [keyInput];\n const normalizedKeys = keys as readonly string[];\n\n if (normalizedKeys.length === 0) {\n throw new EnvaptError(EnvaptErrorCodes.InvalidKeyInput, 'At least one environment key must be provided.');\n }\n\n if (normalizedKeys.some((k) => typeof k !== 'string')) {\n throw new EnvaptError(EnvaptErrorCodes.InvalidKeyInput, 'Environment keys must be strings.');\n }\n\n if (normalizedKeys.some((k) => k.trim() === '')) {\n throw new EnvaptError(EnvaptErrorCodes.InvalidKeyInput, 'Environment keys cannot be empty strings.');\n }\n\n for (const candidate of normalizedKeys) {\n const value = this.config.get(candidate) as string | undefined;\n if (value !== undefined) {\n return { key: candidate, value };\n }\n }\n\n return { key: normalizedKeys[0] as string, value: undefined };\n }\n\n protected static get config(): Map<string, unknown> {\n if (EnvaptCache.size === 0) {\n const source = EnvapterBase._source;\n // Clone so the loader and downstream reads never mutate the source's backing object.\n const isolatedEnv: Record<string, string> = { ...source.readVars() };\n\n let added = new Set<string>();\n // Sources without a filesystem (injected objects on the browser or Workers) skip the\n // .env cascade, profiles, and envPaths; only the readVars() snapshot populates the cache.\n if (source.supportsFiles) {\n debugVerbose(`base dir: ${EnvapterBase._baseDir ?? 'working directory'}`);\n // Outside the try below so a missing configured profile path surfaces its EnvaptError; only dotenv parse errors stay caught.\n const effectivePaths = this.resolveEffectivePaths();\n debugVerbose(\n `effective .env paths: ${effectivePaths.length === 0 ? '(none)' : effectivePaths.join(', ')}`\n );\n try {\n added = loadDotenv({\n ...this._userDefinedEnvFileOptions,\n path: effectivePaths,\n processEnv: isolatedEnv,\n readFile: source.readFile.bind(source)\n });\n } catch {}\n }\n EnvapterBase._dotenvAddedKeys = added;\n for (const [key, value] of Object.entries(isolatedEnv)) EnvaptCache.set(key, value);\n debugVerbose(`cache populated: ${EnvaptCache.size} keys total`);\n if (EnvapterBase._syncProcessEnv) this.mirrorToProcessEnv();\n }\n\n return EnvaptCache;\n }\n\n /**\n * Eagerly load the `.env` cascade now instead of lazily on the first read. Idempotent: a no-op\n * once the cache is built. Useful before mirroring to `process.env` (see {@link syncProcessEnv}),\n * which is what the `envapt/config` side-effect entry does.\n */\n static load(): void {\n void this.config;\n }\n\n /**\n * Bind the environment {@link EnvSource}. On Node the entry binds {@link NodeEnvSource} for you\n * (a `process.env` snapshot plus the `.env` cascade); on the browser or Workers, pass a\n * `ManualEnvSource` / `WorkerEnvSource` (or any `EnvSource`) before reading. Clears and rebuilds\n * the cache.\n */\n static useSource(source: EnvSource): void {\n EnvapterBase._source = source;\n bindRuntimeFromSource(source);\n this.refreshCache();\n }\n\n /**\n * Read an environment variable as its raw string, skipping parsing and conversion.\n */\n getRaw(key: EnvKeyInput): string | undefined {\n return EnvapterBase.resolveKeyInput(key).value;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EnvapterBase.mjs","names":[],"sources":["../../../src/core/EnvapterBase.ts"],"sourcesContent":["import { Validator } from '../engine/Validators';\nimport { debugVerbose, getDebugLevel, setDebugLevel } from '../infra/Debug';\nimport { loadDotenv } from '../infra/Dotenv';\nimport { EnvaptError, EnvaptErrorCodes } from '../infra/Error';\nimport { bindRuntimeFromSource } from '../infra/runtime';\nimport { UnboundEnvSource } from '../sources/UnboundEnvSource';\n\nimport type { DebugLevel } from '../infra/Debug';\nimport type { EnvFileOptions } from '../infra/Dotenv';\nimport type { EnvKeyInput, FileEnvSource, Source } from '../types';\n\n/** @internal */\nexport const EnvaptCache = new Map<string, unknown>();\n\n/** @internal */\nexport abstract class EnvapterBase {\n protected static _envPaths: string[] = ['.env'];\n protected static _envPathsExplicitlySet = false;\n protected static _baseDir: string | undefined = undefined;\n protected static _userDefinedEnvFileOptions: EnvFileOptions = {};\n protected static _strict = false;\n protected static _syncProcessEnv = false;\n // Loader-written keys only (collisions skipped). Refilled on every cache rebuild.\n protected static _dotenvAddedKeys: Set<string> = new Set<string>();\n // Unbound by default so non-Node builds throw NoSourceBound on read until useSource() is called.\n // NodeEnvapter's static block binds NodeEnvSource when referenced, so `import 'envapt'` needs no setup.\n protected static _source: Source = new UnboundEnvSource();\n\n /**\n * Enable or disable strict mode. Default `false`. Setting refreshes the cache so\n * previously-cached converted values get re-evaluated under the new rule.\n */\n static set strict(value: boolean) {\n // Anchored to EnvapterBase: `this._strict` would write an own-property on the subclass that base readers miss.\n EnvapterBase._strict = value;\n // rebuild via `this` so the subclass `resolveEffectivePaths` override is honored (EnvapterBase would skip it).\n this.refreshCache();\n }\n\n static get strict(): boolean {\n return EnvapterBase._strict;\n }\n\n /**\n * Set the debug log level. Defaults to `silent`. When unset, reads `ENVAPT_DEBUG` from the\n * bound source on first access. The setter overrides any env-var value. Output goes to stderr\n * on Node (the console elsewhere), prefixed with `[envapt]`.\n */\n static set debug(level: DebugLevel) {\n setDebugLevel(level);\n }\n\n static get debug(): DebugLevel {\n return getDebugLevel();\n }\n\n /**\n * Opt-in mirror of dotenv-loaded keys back to `process.env`. Default `false`.\n *\n * Only keys the loader actually wrote are mirrored, so collision behavior follows\n * `envFileOptions.override`: with the default `false`, pre-existing `process.env` values\n * are preserved. With `true`, the file value wins in both the cache and the mirror.\n *\n * Flipping `false → true` mirrors the existing tracked delta immediately (no cache\n * refresh). Flipping `true → false` is one-way: previously mirrored keys remain in\n * `process.env` until the process exits.\n */\n static set syncProcessEnv(value: boolean) {\n Validator.validateSyncProcessEnv(value);\n const previous = EnvapterBase._syncProcessEnv;\n // Anchored to EnvapterBase: `this._syncProcessEnv` would write an own-property on the subclass that base readers miss.\n EnvapterBase._syncProcessEnv = value;\n if (!previous && value && EnvaptCache.size > 0) this.mirrorToProcessEnv();\n }\n\n static get syncProcessEnv(): boolean {\n return EnvapterBase._syncProcessEnv;\n }\n\n protected static treatAsMissing(value: string | undefined): boolean {\n if (value === undefined || value === '') return true;\n if (EnvapterBase._strict && value.trim() === '') return true;\n return false;\n }\n\n // No baseDir: candidate returned unchanged so the source resolves it against its own default\n // (process.cwd() on Node). Resolution goes through the source to keep this class node-free.\n protected static resolveAgainstBase(candidate: string): string {\n const baseDir = EnvapterBase._baseDir;\n if (baseDir === undefined) return candidate;\n const source = EnvapterBase._source;\n /* v8 ignore next -- @preserve callers are all file-gated, so the source is never bare here */\n if (!source.supportsFiles) return candidate;\n return source.resolvePath(baseDir, candidate);\n }\n\n // File-based config (envPaths/baseDir/configureProfiles) is meaningless without a filesystem, and\n // it throws instead of silently ignoring it on the browser or Workers. Narrows the source so callers\n // can reach the file capabilities (resolvePath/normalizeBaseDir) after the check.\n protected static assertFileApiSupported(api: string, source: Source): asserts source is FileEnvSource {\n if (!source.supportsFiles) {\n throw new EnvaptError(\n EnvaptErrorCodes.FileApiUnsupported,\n `${api} requires a filesystem-backed source; the bound source does not support .env files.`\n );\n }\n }\n\n // Existence via the bound source instead of fs.existsSync/accessSync: a file \"exists\" when the\n // source can read it.\n protected static sourceFileExists(path: string): boolean {\n const source = EnvapterBase._source;\n /* v8 ignore next -- @preserve every caller is file-gated, so this never sees a bare source */\n if (!source.supportsFiles) return false;\n return source.readFile(path, 'utf8') !== undefined;\n }\n\n protected static refreshCache(): void {\n EnvaptCache.clear();\n EnvapterBase._dotenvAddedKeys = new Set();\n debugVerbose('cache cleared, reloading config');\n void this.config; // getter rebuilds the cache as a side effect\n }\n\n protected static mirrorToProcessEnv(): void {\n if (EnvapterBase._dotenvAddedKeys.size === 0) return;\n const source = EnvapterBase._source;\n /* v8 ignore next -- @preserve dotenv keys only accumulate under a file source, so the delta implies supportsFiles here */\n if (!source.supportsFiles) return;\n const mirrored: Record<string, string> = {};\n for (const key of EnvapterBase._dotenvAddedKeys) {\n const value = EnvaptCache.get(key);\n /* v8 ignore next -- @preserve loader only writes strings, defensive against future cache contents */\n if (typeof value !== 'string') continue;\n mirrored[key] = this.resolveForMirror(key, value);\n debugVerbose(`mirrored ${key} to the ambient environment`);\n }\n source.writeVars(mirrored);\n debugVerbose(`mirrored ${EnvapterBase._dotenvAddedKeys.size} keys to the ambient environment`);\n }\n\n // The template resolver is defined in PrimitiveMethods, and EnvapterBase can't call it without an\n // import cycle, so the mirror expands ${VAR} through this override.\n protected static resolveForMirror(_key: string, value: string): string {\n /* v8 ignore next -- @preserve overridden by PrimitiveMethods on every concrete class */\n return value;\n }\n\n // Default returns the explicit `_envPaths`. EnvironmentMethods overrides to layer the dotenv-flow\n // cascade + configureProfiles when envPaths was never explicitly set.\n protected static resolveEffectivePaths(): string[] {\n /* v8 ignore next -- @preserve */\n return this._envPaths.map((p) => this.resolveAgainstBase(p));\n }\n\n protected static resolveKeyInput(keyInput: EnvKeyInput): { key: string; value: string | undefined } {\n const keys = Array.isArray(keyInput) ? keyInput : [keyInput];\n const normalizedKeys = keys as readonly string[];\n\n if (normalizedKeys.length === 0) {\n throw new EnvaptError(EnvaptErrorCodes.InvalidKeyInput, 'At least one environment key must be provided.');\n }\n\n if (normalizedKeys.some((k) => typeof k !== 'string')) {\n throw new EnvaptError(EnvaptErrorCodes.InvalidKeyInput, 'Environment keys must be strings.');\n }\n\n if (normalizedKeys.some((k) => k.trim() === '')) {\n throw new EnvaptError(EnvaptErrorCodes.InvalidKeyInput, 'Environment keys cannot be empty strings.');\n }\n\n for (const candidate of normalizedKeys) {\n const value = this.config.get(candidate) as string | undefined;\n if (value !== undefined) {\n return { key: candidate, value };\n }\n }\n\n return { key: normalizedKeys[0] as string, value: undefined };\n }\n\n protected static get config(): Map<string, unknown> {\n if (EnvaptCache.size === 0) {\n const source = EnvapterBase._source;\n // Clone so the loader and downstream reads never mutate the source's backing object.\n const isolatedEnv: Record<string, string> = { ...source.readVars() };\n\n let added = new Set<string>();\n // Sources without a filesystem (injected objects on the browser or Workers) skip the\n // .env cascade, profiles, and envPaths. Only the readVars() snapshot populates the cache.\n if (source.supportsFiles) {\n debugVerbose(`base dir: ${EnvapterBase._baseDir ?? 'working directory'}`);\n // Outside the try below so a missing configured profile path surfaces its EnvaptError. Only dotenv parse errors stay caught.\n const effectivePaths = this.resolveEffectivePaths();\n debugVerbose(\n `effective .env paths: ${effectivePaths.length === 0 ? '(none)' : effectivePaths.join(', ')}`\n );\n try {\n added = loadDotenv({\n ...this._userDefinedEnvFileOptions,\n path: effectivePaths,\n processEnv: isolatedEnv,\n readFile: source.readFile.bind(source)\n });\n } catch {}\n }\n EnvapterBase._dotenvAddedKeys = added;\n for (const [key, value] of Object.entries(isolatedEnv)) EnvaptCache.set(key, value);\n debugVerbose(`cache populated: ${EnvaptCache.size} keys total`);\n if (EnvapterBase._syncProcessEnv) this.mirrorToProcessEnv();\n }\n\n return EnvaptCache;\n }\n\n /**\n * Eagerly load the `.env` cascade now instead of lazily on the first read. Idempotent: a no-op\n * once the cache is built. Useful before mirroring to `process.env` (see {@link syncProcessEnv}),\n * which is what the `envapt/config` side-effect entry does.\n */\n static load(): void {\n void this.config;\n }\n\n /**\n * Bind the environment {@link Source}. On Node the entry binds {@link NodeEnvSource} for you\n * (a `process.env` snapshot plus the `.env` cascade). On the browser or Workers, pass a\n * `PortableSource` (or any `Source`) before reading. Clears and rebuilds the cache.\n */\n static useSource(source: Source): void {\n EnvapterBase._source = source;\n bindRuntimeFromSource(source);\n this.refreshCache();\n }\n\n /**\n * Read an environment variable as its raw string, skipping parsing and conversion.\n */\n getRaw(key: EnvKeyInput): string | undefined {\n return EnvapterBase.resolveKeyInput(key).value;\n }\n}\n"],"mappings":"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"}
|
package/dist/node/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./converters/Converters.cjs"),t=require("./infra/Error.cjs"),n=require("./core/EnvironmentMethods.cjs"),r=require("./sources/
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./converters/Converters.cjs"),t=require("./infra/Error.cjs"),n=require("./core/EnvironmentMethods.cjs"),r=require("./sources/PortableSource.cjs"),i=require("./sources/ManualEnvSource.cjs"),a=require("./sources/WorkerEnvSource.cjs"),o=require("./decorators/modern/Envapt.cjs"),s=require("./decorators/modern/SugarDecorators.cjs"),c=require("./sources/NodeEnvSource.cjs"),l=require("./engine/NodeEnvapter.cjs");exports.Converters=e.Converters,exports.EnvBool=s.EnvBool,exports.EnvNum=s.EnvNum,exports.EnvStr=s.EnvStr,exports.EnvTime=s.EnvTime,exports.EnvUrl=s.EnvUrl,exports.Envapt=o.Envapt,exports.EnvaptError=t.EnvaptError,exports.EnvaptErrorCodes=t.EnvaptErrorCodes,exports.Envapter=l.NodeEnvapter,exports.Environment=n.Environment,exports.ManualEnvSource=i.ManualEnvSource,exports.NodeEnvSource=c.NodeEnvSource,exports.PortableSource=r.PortableSource,exports.WorkerEnvSource=a.WorkerEnvSource,exports.isArrayOf=e.isArrayOf;
|
package/dist/node/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Converters as e,isArrayOf as t}from"./converters/Converters.mjs";import{EnvaptError as n,EnvaptErrorCodes as r}from"./infra/Error.mjs";import{Environment as i}from"./core/EnvironmentMethods.mjs";import{
|
|
1
|
+
import{Converters as e,isArrayOf as t}from"./converters/Converters.mjs";import{EnvaptError as n,EnvaptErrorCodes as r}from"./infra/Error.mjs";import{Environment as i}from"./core/EnvironmentMethods.mjs";import{PortableSource as a}from"./sources/PortableSource.mjs";import{ManualEnvSource as o}from"./sources/ManualEnvSource.mjs";import{WorkerEnvSource as s}from"./sources/WorkerEnvSource.mjs";import{Envapt as c}from"./decorators/modern/Envapt.mjs";import{EnvBool as l,EnvNum as u,EnvStr as d,EnvTime as f,EnvUrl as p}from"./decorators/modern/SugarDecorators.mjs";import{NodeEnvSource as m}from"./sources/NodeEnvSource.mjs";import{NodeEnvapter as h}from"./engine/NodeEnvapter.mjs";export{e as Converters,l as EnvBool,u as EnvNum,d as EnvStr,f as EnvTime,p as EnvUrl,c as Envapt,n as EnvaptError,r as EnvaptErrorCodes,h as Envapter,i as Environment,o as ManualEnvSource,m as NodeEnvSource,a as PortableSource,s as WorkerEnvSource,t as isArrayOf};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.cjs","names":[],"sources":["../../../src/infra/runtime.ts"],"sourcesContent":["import type {
|
|
1
|
+
{"version":3,"file":"runtime.cjs","names":[],"sources":["../../../src/infra/runtime.ts"],"sourcesContent":["import type { Source } from '../types';\n\n// importing Debug back into EnvapterBase would cycle, so the source and sink are injected via the setters below.\n/* v8 ignore start -- @preserve replaced at load on Node, so the Node suite never runs these defaults */\nlet sink: (line: string) => void = (line) => {\n // eslint-disable-next-line no-console -- the off-Node fallback log sink\n console.error(line);\n};\nlet envReader: () => Record<string, string> = () => ({});\n/* v8 ignore stop */\n\nexport function setRuntimeSink(fn: (line: string) => void): void {\n sink = fn;\n}\n\nexport function writeRuntimeLine(line: string): void {\n sink(line);\n}\n\nexport function readRuntimeEnv(): Record<string, string> {\n return envReader();\n}\n\nexport function bindRuntimeFromSource(source: Source): void {\n envReader = (): Record<string, string> => source.readVars();\n}\n"],"mappings":"AAIA,IAAI,EAAgC,GAAS,CAEzC,QAAQ,MAAM,CAAI,CACtB,EACI,OAAiD,CAAC,GAGtD,SAAgB,EAAe,EAAkC,CAC7D,EAAO,CACX,CAEA,SAAgB,EAAiB,EAAoB,CACjD,EAAK,CAAI,CACb,CAEA,SAAgB,GAAyC,CACrD,OAAO,EAAU,CACrB,CAEA,SAAgB,EAAsB,EAAsB,CACxD,MAA0C,EAAO,SAAS,CAC9D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.mjs","names":[],"sources":["../../../src/infra/runtime.ts"],"sourcesContent":["import type {
|
|
1
|
+
{"version":3,"file":"runtime.mjs","names":[],"sources":["../../../src/infra/runtime.ts"],"sourcesContent":["import type { Source } from '../types';\n\n// importing Debug back into EnvapterBase would cycle, so the source and sink are injected via the setters below.\n/* v8 ignore start -- @preserve replaced at load on Node, so the Node suite never runs these defaults */\nlet sink: (line: string) => void = (line) => {\n // eslint-disable-next-line no-console -- the off-Node fallback log sink\n console.error(line);\n};\nlet envReader: () => Record<string, string> = () => ({});\n/* v8 ignore stop */\n\nexport function setRuntimeSink(fn: (line: string) => void): void {\n sink = fn;\n}\n\nexport function writeRuntimeLine(line: string): void {\n sink(line);\n}\n\nexport function readRuntimeEnv(): Record<string, string> {\n return envReader();\n}\n\nexport function bindRuntimeFromSource(source: Source): void {\n envReader = (): Record<string, string> => source.readVars();\n}\n"],"mappings":"AAIA,IAAI,EAAgC,GAAS,CAEzC,QAAQ,MAAM,CAAI,CACtB,EACI,OAAiD,CAAC,GAGtD,SAAgB,EAAe,EAAkC,CAC7D,EAAO,CACX,CAEA,SAAgB,EAAiB,EAAoB,CACjD,EAAK,CAAI,CACb,CAEA,SAAgB,GAAyC,CACrD,OAAO,EAAU,CACrB,CAEA,SAAgB,EAAsB,EAAsB,CACxD,MAA0C,EAAO,SAAS,CAC9D"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./
|
|
1
|
+
const e=require("./PortableSource.cjs");var t=class extends e.PortableSource{};exports.ManualEnvSource=t;
|
|
2
2
|
//# sourceMappingURL=ManualEnvSource.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ManualEnvSource.cjs","names":["
|
|
1
|
+
{"version":3,"file":"ManualEnvSource.cjs","names":["PortableSource"],"sources":["../../../src/sources/ManualEnvSource.ts"],"sourcesContent":["import { PortableSource } from './PortableSource';\n\n/**\n * @deprecated Renamed to {@link PortableSource}, which serves every no-filesystem runtime the same way.\n * Removed in v8. Replace `new ManualEnvSource(obj)` with `new PortableSource(obj)`.\n * @public\n */\nexport class ManualEnvSource extends PortableSource {}\n"],"mappings":"wCAOA,IAAa,EAAb,cAAqCA,EAAAA,cAAe,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{PortableSource as e}from"./PortableSource.mjs";var t=class extends e{};export{t as ManualEnvSource};
|
|
2
2
|
//# sourceMappingURL=ManualEnvSource.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ManualEnvSource.mjs","names":[],"sources":["../../../src/sources/ManualEnvSource.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"ManualEnvSource.mjs","names":[],"sources":["../../../src/sources/ManualEnvSource.ts"],"sourcesContent":["import { PortableSource } from './PortableSource';\n\n/**\n * @deprecated Renamed to {@link PortableSource}, which serves every no-filesystem runtime the same way.\n * Removed in v8. Replace `new ManualEnvSource(obj)` with `new PortableSource(obj)`.\n * @public\n */\nexport class ManualEnvSource extends PortableSource {}\n"],"mappings":"sDAOA,IAAa,EAAb,cAAqC,CAAe,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PortableSource.cjs","names":["coerceToStringRecord"],"sources":["../../../src/sources/PortableSource.ts"],"sourcesContent":["import { coerceToStringRecord } from './coerce';\n\nimport type { BareEnvSource } from '../types';\n\n/**\n * Environment source seeded from an object you provide, for every runtime without a filesystem. The\n * config your bundler injects into a browser build (Vite's `import.meta.env`, a webpack `DefinePlugin`\n * global), the Cloudflare `env` binding, a Vercel Edge or Fastly config object, a test fixture, or any\n * plain object. Pass it straight through. Non-string values are JSON-stringified so the converters\n * still apply, which means they must be JSON-serializable. Without a filesystem the `.env` cascade and\n * file APIs do not apply.\n * @public\n */\nexport class PortableSource implements BareEnvSource {\n /** Always `false`. With no filesystem, the `.env` cascade and file APIs do not apply. */\n readonly supportsFiles = false;\n private readonly vars: Record<string, string>;\n\n /** Seed the source from `vars`. Non-string values are JSON-stringified so the converters still apply. */\n constructor(vars: object) {\n this.vars = coerceToStringRecord(vars);\n }\n\n /** Returns a snapshot clone of the provided object as plain strings. */\n readVars(): Record<string, string> {\n return { ...this.vars };\n }\n}\n"],"mappings":"gCAaA,IAAa,EAAb,KAAqD,CAEjD,cAAyB,GACzB,KAGA,YAAY,EAAc,CACtB,KAAK,KAAOA,EAAAA,qBAAqB,CAAI,CACzC,CAGA,UAAmC,CAC/B,MAAO,CAAE,GAAG,KAAK,IAAK,CAC1B,CACJ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PortableSource.mjs","names":[],"sources":["../../../src/sources/PortableSource.ts"],"sourcesContent":["import { coerceToStringRecord } from './coerce';\n\nimport type { BareEnvSource } from '../types';\n\n/**\n * Environment source seeded from an object you provide, for every runtime without a filesystem. The\n * config your bundler injects into a browser build (Vite's `import.meta.env`, a webpack `DefinePlugin`\n * global), the Cloudflare `env` binding, a Vercel Edge or Fastly config object, a test fixture, or any\n * plain object. Pass it straight through. Non-string values are JSON-stringified so the converters\n * still apply, which means they must be JSON-serializable. Without a filesystem the `.env` cascade and\n * file APIs do not apply.\n * @public\n */\nexport class PortableSource implements BareEnvSource {\n /** Always `false`. With no filesystem, the `.env` cascade and file APIs do not apply. */\n readonly supportsFiles = false;\n private readonly vars: Record<string, string>;\n\n /** Seed the source from `vars`. Non-string values are JSON-stringified so the converters still apply. */\n constructor(vars: object) {\n this.vars = coerceToStringRecord(vars);\n }\n\n /** Returns a snapshot clone of the provided object as plain strings. */\n readVars(): Record<string, string> {\n return { ...this.vars };\n }\n}\n"],"mappings":"oDAaA,IAAa,EAAb,KAAqD,CAEjD,cAAyB,GACzB,KAGA,YAAY,EAAc,CACtB,KAAK,KAAO,EAAqB,CAAI,CACzC,CAGA,UAAmC,CAC/B,MAAO,CAAE,GAAG,KAAK,IAAK,CAC1B,CACJ"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../infra/Error.cjs");var t=class{supportsFiles=!1;readVars(){throw new e.EnvaptError(307,`No environment source is bound. Call Envapter.useSource(...) with a source such as new
|
|
1
|
+
const e=require("../infra/Error.cjs");var t=class{supportsFiles=!1;readVars(){throw new e.EnvaptError(307,`No environment source is bound. Call Envapter.useSource(...) with a source such as new PortableSource({...}) before reading.`)}};exports.UnboundEnvSource=t;
|
|
2
2
|
//# sourceMappingURL=UnboundEnvSource.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UnboundEnvSource.cjs","names":["EnvaptError"],"sources":["../../../src/sources/UnboundEnvSource.ts"],"sourcesContent":["import { EnvaptError, EnvaptErrorCodes } from '../infra/Error';\n\nimport type { BareEnvSource } from '../types';\n\n/**\n * The default source until one is bound. Every read throws NoSourceBound so a forgotten `useSource`\n * surfaces at read time instead of silently yielding nothing. The Node entry binds {@link NodeEnvSource}\n * at load
|
|
1
|
+
{"version":3,"file":"UnboundEnvSource.cjs","names":["EnvaptError"],"sources":["../../../src/sources/UnboundEnvSource.ts"],"sourcesContent":["import { EnvaptError, EnvaptErrorCodes } from '../infra/Error';\n\nimport type { BareEnvSource } from '../types';\n\n/**\n * The default source until one is bound. Every read throws NoSourceBound so a forgotten `useSource`\n * surfaces at read time instead of silently yielding nothing. The Node entry binds {@link NodeEnvSource}\n * at load, browser and Workers bundles must call `Envapter.useSource(...)` before reading.\n * @internal\n */\nexport class UnboundEnvSource implements BareEnvSource {\n readonly supportsFiles = false;\n\n readVars(): Record<string, string> {\n throw new EnvaptError(\n EnvaptErrorCodes.NoSourceBound,\n 'No environment source is bound. Call Envapter.useSource(...) with a source such as new PortableSource({...}) before reading.'\n );\n }\n}\n"],"mappings":"sCAUA,IAAa,EAAb,KAAuD,CACnD,cAAyB,GAEzB,UAAmC,CAC/B,MAAM,IAAIA,EAAAA,YAAAA,IAEN,8HACJ,CACJ,CACJ"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{EnvaptError as e}from"../infra/Error.mjs";var t=class{supportsFiles=!1;readVars(){throw new e(307,`No environment source is bound. Call Envapter.useSource(...) with a source such as new
|
|
1
|
+
import{EnvaptError as e}from"../infra/Error.mjs";var t=class{supportsFiles=!1;readVars(){throw new e(307,`No environment source is bound. Call Envapter.useSource(...) with a source such as new PortableSource({...}) before reading.`)}};export{t as UnboundEnvSource};
|
|
2
2
|
//# sourceMappingURL=UnboundEnvSource.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UnboundEnvSource.mjs","names":[],"sources":["../../../src/sources/UnboundEnvSource.ts"],"sourcesContent":["import { EnvaptError, EnvaptErrorCodes } from '../infra/Error';\n\nimport type { BareEnvSource } from '../types';\n\n/**\n * The default source until one is bound. Every read throws NoSourceBound so a forgotten `useSource`\n * surfaces at read time instead of silently yielding nothing. The Node entry binds {@link NodeEnvSource}\n * at load
|
|
1
|
+
{"version":3,"file":"UnboundEnvSource.mjs","names":[],"sources":["../../../src/sources/UnboundEnvSource.ts"],"sourcesContent":["import { EnvaptError, EnvaptErrorCodes } from '../infra/Error';\n\nimport type { BareEnvSource } from '../types';\n\n/**\n * The default source until one is bound. Every read throws NoSourceBound so a forgotten `useSource`\n * surfaces at read time instead of silently yielding nothing. The Node entry binds {@link NodeEnvSource}\n * at load, browser and Workers bundles must call `Envapter.useSource(...)` before reading.\n * @internal\n */\nexport class UnboundEnvSource implements BareEnvSource {\n readonly supportsFiles = false;\n\n readVars(): Record<string, string> {\n throw new EnvaptError(\n EnvaptErrorCodes.NoSourceBound,\n 'No environment source is bound. Call Envapter.useSource(...) with a source such as new PortableSource({...}) before reading.'\n );\n }\n}\n"],"mappings":"iDAUA,IAAa,EAAb,KAAuD,CACnD,cAAyB,GAEzB,UAAmC,CAC/B,MAAM,IAAI,EAAA,IAEN,8HACJ,CACJ,CACJ"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./
|
|
1
|
+
const e=require("./PortableSource.cjs");var t=class extends e.PortableSource{};exports.WorkerEnvSource=t;
|
|
2
2
|
//# sourceMappingURL=WorkerEnvSource.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkerEnvSource.cjs","names":["
|
|
1
|
+
{"version":3,"file":"WorkerEnvSource.cjs","names":["PortableSource"],"sources":["../../../src/sources/WorkerEnvSource.ts"],"sourcesContent":["import { PortableSource } from './PortableSource';\n\n/**\n * @deprecated Use {@link PortableSource} instead, which wraps the Cloudflare `env` binding identically.\n * Removed in v8. Replace `new WorkerEnvSource(env)` with `new PortableSource(env)`.\n * @public\n */\nexport class WorkerEnvSource extends PortableSource {}\n"],"mappings":"wCAOA,IAAa,EAAb,cAAqCA,EAAAA,cAAe,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{PortableSource as e}from"./PortableSource.mjs";var t=class extends e{};export{t as WorkerEnvSource};
|
|
2
2
|
//# sourceMappingURL=WorkerEnvSource.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkerEnvSource.mjs","names":[],"sources":["../../../src/sources/WorkerEnvSource.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"WorkerEnvSource.mjs","names":[],"sources":["../../../src/sources/WorkerEnvSource.ts"],"sourcesContent":["import { PortableSource } from './PortableSource';\n\n/**\n * @deprecated Use {@link PortableSource} instead, which wraps the Cloudflare `env` binding identically.\n * Removed in v8. Replace `new WorkerEnvSource(env)` with `new PortableSource(env)`.\n * @public\n */\nexport class WorkerEnvSource extends PortableSource {}\n"],"mappings":"sDAOA,IAAa,EAAb,cAAqC,CAAe,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coerce.cjs","names":[],"sources":["../../../src/sources/coerce.ts"],"sourcesContent":["// Shared by
|
|
1
|
+
{"version":3,"file":"coerce.cjs","names":[],"sources":["../../../src/sources/coerce.ts"],"sourcesContent":["// Shared by PortableSource and its deprecated aliases. The returned record is fresh, so a caller\n// mutating its object later cannot leak into the source, and non-string values are JSON-stringified.\nexport function coerceToStringRecord(env: object): Record<string, string> {\n const snapshot: Record<string, string> = {};\n // `object` so a Cloudflare `Env` (an interface with no index signature) is accepted. cast to a record\n // to read entries as `unknown` rather than `any`.\n for (const [key, value] of Object.entries(env as Record<string, unknown>)) {\n if (typeof value === 'string') {\n snapshot[key] = value;\n continue;\n }\n const encoded = JSON.stringify(value);\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- TS lib mistypes JSON.stringify's return as always-string; it is string | undefined at runtime\n if (encoded !== undefined) snapshot[key] = encoded;\n }\n return snapshot;\n}\n"],"mappings":"AAEA,SAAgB,EAAqB,EAAqC,CACtE,IAAM,EAAmC,CAAC,EAG1C,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,CAA8B,EAAG,CACvE,GAAI,OAAO,GAAU,SAAU,CAC3B,EAAS,GAAO,EAChB,QACJ,CACA,IAAM,EAAU,KAAK,UAAU,CAAK,EAEhC,IAAY,IAAA,KAAW,EAAS,GAAO,EAC/C,CACA,OAAO,CACX"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coerce.mjs","names":[],"sources":["../../../src/sources/coerce.ts"],"sourcesContent":["// Shared by
|
|
1
|
+
{"version":3,"file":"coerce.mjs","names":[],"sources":["../../../src/sources/coerce.ts"],"sourcesContent":["// Shared by PortableSource and its deprecated aliases. The returned record is fresh, so a caller\n// mutating its object later cannot leak into the source, and non-string values are JSON-stringified.\nexport function coerceToStringRecord(env: object): Record<string, string> {\n const snapshot: Record<string, string> = {};\n // `object` so a Cloudflare `Env` (an interface with no index signature) is accepted. cast to a record\n // to read entries as `unknown` rather than `any`.\n for (const [key, value] of Object.entries(env as Record<string, unknown>)) {\n if (typeof value === 'string') {\n snapshot[key] = value;\n continue;\n }\n const encoded = JSON.stringify(value);\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- TS lib mistypes JSON.stringify's return as always-string; it is string | undefined at runtime\n if (encoded !== undefined) snapshot[key] = encoded;\n }\n return snapshot;\n}\n"],"mappings":"AAEA,SAAgB,EAAqB,EAAqC,CACtE,IAAM,EAAmC,CAAC,EAG1C,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,CAA8B,EAAG,CACvE,GAAI,OAAO,GAAU,SAAU,CAC3B,EAAS,GAAO,EAChB,QACJ,CACA,IAAM,EAAU,KAAK,UAAU,CAAK,EAEhC,IAAY,IAAA,KAAW,EAAS,GAAO,EAC/C,CACA,OAAO,CACX"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DebugLevel } from "../infra/Debug.mjs";
|
|
2
2
|
import { EnvFileOptions } from "../infra/Dotenv.mjs";
|
|
3
3
|
import { EnvKeyInput } from "../types/Env.mjs";
|
|
4
|
-
import {
|
|
4
|
+
import { FileEnvSource, Source } from "../types/Source.mjs";
|
|
5
5
|
|
|
6
6
|
//#region src/core/EnvapterBase.d.ts
|
|
7
7
|
/** @internal */
|
|
@@ -13,7 +13,7 @@ declare abstract class EnvapterBase {
|
|
|
13
13
|
protected static _strict: boolean;
|
|
14
14
|
protected static _syncProcessEnv: boolean;
|
|
15
15
|
protected static _dotenvAddedKeys: Set<string>;
|
|
16
|
-
protected static _source:
|
|
16
|
+
protected static _source: Source;
|
|
17
17
|
/**
|
|
18
18
|
* Enable or disable strict mode. Default `false`. Setting refreshes the cache so
|
|
19
19
|
* previously-cached converted values get re-evaluated under the new rule.
|
|
@@ -22,7 +22,7 @@ declare abstract class EnvapterBase {
|
|
|
22
22
|
static get strict(): boolean;
|
|
23
23
|
/**
|
|
24
24
|
* Set the debug log level. Defaults to `silent`. When unset, reads `ENVAPT_DEBUG` from the
|
|
25
|
-
* bound source on first access
|
|
25
|
+
* bound source on first access. The setter overrides any env-var value. Output goes to stderr
|
|
26
26
|
* on Node (the console elsewhere), prefixed with `[envapt]`.
|
|
27
27
|
*/
|
|
28
28
|
static set debug(level: DebugLevel);
|
|
@@ -32,7 +32,7 @@ declare abstract class EnvapterBase {
|
|
|
32
32
|
*
|
|
33
33
|
* Only keys the loader actually wrote are mirrored, so collision behavior follows
|
|
34
34
|
* `envFileOptions.override`: with the default `false`, pre-existing `process.env` values
|
|
35
|
-
* are preserved
|
|
35
|
+
* are preserved. With `true`, the file value wins in both the cache and the mirror.
|
|
36
36
|
*
|
|
37
37
|
* Flipping `false → true` mirrors the existing tracked delta immediately (no cache
|
|
38
38
|
* refresh). Flipping `true → false` is one-way: previously mirrored keys remain in
|
|
@@ -42,7 +42,7 @@ declare abstract class EnvapterBase {
|
|
|
42
42
|
static get syncProcessEnv(): boolean;
|
|
43
43
|
protected static treatAsMissing(value: string | undefined): boolean;
|
|
44
44
|
protected static resolveAgainstBase(candidate: string): string;
|
|
45
|
-
protected static assertFileApiSupported(api: string, source:
|
|
45
|
+
protected static assertFileApiSupported(api: string, source: Source): asserts source is FileEnvSource;
|
|
46
46
|
protected static sourceFileExists(path: string): boolean;
|
|
47
47
|
protected static refreshCache(): void;
|
|
48
48
|
protected static mirrorToProcessEnv(): void;
|
|
@@ -60,12 +60,11 @@ declare abstract class EnvapterBase {
|
|
|
60
60
|
*/
|
|
61
61
|
static load(): void;
|
|
62
62
|
/**
|
|
63
|
-
* Bind the environment {@link
|
|
64
|
-
* (a `process.env` snapshot plus the `.env` cascade)
|
|
65
|
-
* `
|
|
66
|
-
* the cache.
|
|
63
|
+
* Bind the environment {@link Source}. On Node the entry binds {@link NodeEnvSource} for you
|
|
64
|
+
* (a `process.env` snapshot plus the `.env` cascade). On the browser or Workers, pass a
|
|
65
|
+
* `PortableSource` (or any `Source`) before reading. Clears and rebuilds the cache.
|
|
67
66
|
*/
|
|
68
|
-
static useSource(source:
|
|
67
|
+
static useSource(source: Source): void;
|
|
69
68
|
/**
|
|
70
69
|
* Read an environment variable as its raw string, skipping parsing and conversion.
|
|
71
70
|
*/
|
package/dist/types/index.d.mts
CHANGED
|
@@ -7,8 +7,9 @@ import { Err, SchemaConstraint, SchemaMustBeSync } from "./types/Schema.mjs";
|
|
|
7
7
|
import { Environment } from "./core/EnvironmentMethods.mjs";
|
|
8
8
|
import { EnvProfile, EnvaptOptions, ProfilesConfig } from "./types/Options.mjs";
|
|
9
9
|
import { EnvKeyInput } from "./types/Env.mjs";
|
|
10
|
-
import { BareEnvSource, EnvSource, FileEnvSource } from "./types/Source.mjs";
|
|
10
|
+
import { BareEnvSource, EnvSource, FileEnvSource, Source } from "./types/Source.mjs";
|
|
11
11
|
import { EnvaptAccessorDecorator, EnvaptFieldDecorator } from "./types/Decorator.mjs";
|
|
12
|
+
import { PortableSource } from "./sources/PortableSource.mjs";
|
|
12
13
|
import { ManualEnvSource } from "./sources/ManualEnvSource.mjs";
|
|
13
14
|
import { WorkerEnvSource } from "./sources/WorkerEnvSource.mjs";
|
|
14
15
|
import { EnvaptError, EnvaptErrorCodes } from "./infra/Error.mjs";
|
|
@@ -16,4 +17,4 @@ import { Envapt } from "./decorators/modern/Envapt.mjs";
|
|
|
16
17
|
import { EnvBool, EnvNum, EnvStr, EnvTime, EnvUrl } from "./decorators/modern/SugarDecorators.mjs";
|
|
17
18
|
import { NodeEnvapter } from "./engine/NodeEnvapter.mjs";
|
|
18
19
|
import { NodeEnvSource } from "./sources/NodeEnvSource.mjs";
|
|
19
|
-
export { type AdvancedConverterReturn, type ArrayElement, type ArrayOf, type BareEnvSource, type BuiltInConverter, type BuiltInConverterFunction, type ConditionalReturn, type ConverterFunction, type ConverterToken, Converters, type CustomElementConverter, type DebugLevel, EnvBool, type EnvFileOptions, type EnvKeyInput, EnvNum, type EnvProfile, type EnvSource, EnvStr, EnvTime, EnvUrl, Envapt, type EnvaptAccessorDecorator, type EnvaptConverter, EnvaptError, EnvaptErrorCodes, type EnvaptFieldDecorator, type EnvaptOptions, NodeEnvapter as Envapter, Environment, type Err, type FileEnvSource, type InferConverterFallbackType, type InferConverterReturnType, type InferPrimitiveReturnType, type InferSchemaInput, type InferSchemaOutput, type JsonValue, ManualEnvSource, type MapOfConverterFunctions, NodeEnvSource, type PrimitiveConstructor, type ProfilesConfig, type SchemaConstraint, type SchemaMustBeSync, type StandardSchemaV1, type TimeFallback, type TimeUnit, WorkerEnvSource, isArrayOf };
|
|
20
|
+
export { type AdvancedConverterReturn, type ArrayElement, type ArrayOf, type BareEnvSource, type BuiltInConverter, type BuiltInConverterFunction, type ConditionalReturn, type ConverterFunction, type ConverterToken, Converters, type CustomElementConverter, type DebugLevel, EnvBool, type EnvFileOptions, type EnvKeyInput, EnvNum, type EnvProfile, type EnvSource, EnvStr, EnvTime, EnvUrl, Envapt, type EnvaptAccessorDecorator, type EnvaptConverter, EnvaptError, EnvaptErrorCodes, type EnvaptFieldDecorator, type EnvaptOptions, NodeEnvapter as Envapter, Environment, type Err, type FileEnvSource, type InferConverterFallbackType, type InferConverterReturnType, type InferPrimitiveReturnType, type InferSchemaInput, type InferSchemaOutput, type JsonValue, ManualEnvSource, type MapOfConverterFunctions, NodeEnvSource, PortableSource, type PrimitiveConstructor, type ProfilesConfig, type SchemaConstraint, type SchemaMustBeSync, type Source, type StandardSchemaV1, type TimeFallback, type TimeUnit, WorkerEnvSource, isArrayOf };
|
|
@@ -7,12 +7,13 @@ import { Err, SchemaConstraint, SchemaMustBeSync } from "./types/Schema.mjs";
|
|
|
7
7
|
import { Environment } from "./core/EnvironmentMethods.mjs";
|
|
8
8
|
import { EnvProfile, EnvaptOptions, ProfilesConfig } from "./types/Options.mjs";
|
|
9
9
|
import { EnvKeyInput } from "./types/Env.mjs";
|
|
10
|
-
import { BareEnvSource, EnvSource, FileEnvSource } from "./types/Source.mjs";
|
|
10
|
+
import { BareEnvSource, EnvSource, FileEnvSource, Source } from "./types/Source.mjs";
|
|
11
11
|
import { EnvaptAccessorDecorator, EnvaptFieldDecorator } from "./types/Decorator.mjs";
|
|
12
|
+
import { PortableSource } from "./sources/PortableSource.mjs";
|
|
12
13
|
import { ManualEnvSource } from "./sources/ManualEnvSource.mjs";
|
|
13
14
|
import { WorkerEnvSource } from "./sources/WorkerEnvSource.mjs";
|
|
14
15
|
import { EnvaptError, EnvaptErrorCodes } from "./infra/Error.mjs";
|
|
15
16
|
import { Envapt } from "./decorators/modern/Envapt.mjs";
|
|
16
17
|
import { EnvBool, EnvNum, EnvStr, EnvTime, EnvUrl } from "./decorators/modern/SugarDecorators.mjs";
|
|
17
18
|
import { PortableEnvapter } from "./engine/PortableEnvapter.mjs";
|
|
18
|
-
export { type AdvancedConverterReturn, type ArrayElement, type ArrayOf, type BareEnvSource, type BuiltInConverter, type BuiltInConverterFunction, type ConditionalReturn, type ConverterFunction, type ConverterToken, Converters, type CustomElementConverter, type DebugLevel, EnvBool, type EnvFileOptions, type EnvKeyInput, EnvNum, type EnvProfile, type EnvSource, EnvStr, EnvTime, EnvUrl, Envapt, type EnvaptAccessorDecorator, type EnvaptConverter, EnvaptError, EnvaptErrorCodes, type EnvaptFieldDecorator, type EnvaptOptions, PortableEnvapter as Envapter, Environment, type Err, type FileEnvSource, type InferConverterFallbackType, type InferConverterReturnType, type InferPrimitiveReturnType, type InferSchemaInput, type InferSchemaOutput, type JsonValue, ManualEnvSource, type MapOfConverterFunctions, type PrimitiveConstructor, type ProfilesConfig, type SchemaConstraint, type SchemaMustBeSync, type StandardSchemaV1, type TimeFallback, type TimeUnit, WorkerEnvSource, isArrayOf };
|
|
19
|
+
export { type AdvancedConverterReturn, type ArrayElement, type ArrayOf, type BareEnvSource, type BuiltInConverter, type BuiltInConverterFunction, type ConditionalReturn, type ConverterFunction, type ConverterToken, Converters, type CustomElementConverter, type DebugLevel, EnvBool, type EnvFileOptions, type EnvKeyInput, EnvNum, type EnvProfile, type EnvSource, EnvStr, EnvTime, EnvUrl, Envapt, type EnvaptAccessorDecorator, type EnvaptConverter, EnvaptError, EnvaptErrorCodes, type EnvaptFieldDecorator, type EnvaptOptions, PortableEnvapter as Envapter, Environment, type Err, type FileEnvSource, type InferConverterFallbackType, type InferConverterReturnType, type InferPrimitiveReturnType, type InferSchemaInput, type InferSchemaOutput, type JsonValue, ManualEnvSource, type MapOfConverterFunctions, PortableSource, type PrimitiveConstructor, type ProfilesConfig, type SchemaConstraint, type SchemaMustBeSync, type Source, type StandardSchemaV1, type TimeFallback, type TimeUnit, WorkerEnvSource, isArrayOf };
|
|
@@ -1,22 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PortableSource } from "./PortableSource.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/sources/ManualEnvSource.d.ts
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* object. Pass it straight through; non-string values are JSON-stringified so the converters still apply.
|
|
8
|
-
* It has no filesystem, so the `.env` cascade and file-based APIs do not apply.
|
|
5
|
+
* @deprecated Renamed to {@link PortableSource}, which serves every no-filesystem runtime the same way.
|
|
6
|
+
* Removed in v8. Replace `new ManualEnvSource(obj)` with `new PortableSource(obj)`.
|
|
9
7
|
* @public
|
|
10
8
|
*/
|
|
11
|
-
declare class ManualEnvSource
|
|
12
|
-
/** Always `false`. With no filesystem, the `.env` cascade and file APIs do not apply. */
|
|
13
|
-
readonly supportsFiles = false;
|
|
14
|
-
private readonly vars;
|
|
15
|
-
/** Seed the source from `vars`. Non-string values are JSON-stringified so the converters still apply. */
|
|
16
|
-
constructor(vars: Record<string, unknown>);
|
|
17
|
-
/** Returns a snapshot clone of the provided object as plain strings. */
|
|
18
|
-
readVars(): Record<string, string>;
|
|
19
|
-
}
|
|
9
|
+
declare class ManualEnvSource extends PortableSource {}
|
|
20
10
|
//#endregion
|
|
21
11
|
export { ManualEnvSource };
|
|
22
12
|
//# sourceMappingURL=ManualEnvSource.d.mts.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { BareEnvSource } from "../types/Source.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/sources/PortableSource.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Environment source seeded from an object you provide, for every runtime without a filesystem. The
|
|
6
|
+
* config your bundler injects into a browser build (Vite's `import.meta.env`, a webpack `DefinePlugin`
|
|
7
|
+
* global), the Cloudflare `env` binding, a Vercel Edge or Fastly config object, a test fixture, or any
|
|
8
|
+
* plain object. Pass it straight through. Non-string values are JSON-stringified so the converters
|
|
9
|
+
* still apply, which means they must be JSON-serializable. Without a filesystem the `.env` cascade and
|
|
10
|
+
* file APIs do not apply.
|
|
11
|
+
* @public
|
|
12
|
+
*/
|
|
13
|
+
declare class PortableSource implements BareEnvSource {
|
|
14
|
+
/** Always `false`. With no filesystem, the `.env` cascade and file APIs do not apply. */
|
|
15
|
+
readonly supportsFiles = false;
|
|
16
|
+
private readonly vars;
|
|
17
|
+
/** Seed the source from `vars`. Non-string values are JSON-stringified so the converters still apply. */
|
|
18
|
+
constructor(vars: object);
|
|
19
|
+
/** Returns a snapshot clone of the provided object as plain strings. */
|
|
20
|
+
readVars(): Record<string, string>;
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { PortableSource };
|
|
24
|
+
//# sourceMappingURL=PortableSource.d.mts.map
|
|
@@ -1,22 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PortableSource } from "./PortableSource.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/sources/WorkerEnvSource.d.ts
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* JSON-serializable (a `bigint` or circular value throws). No filesystem, so the `.env` cascade and
|
|
8
|
-
* file-based APIs do not apply.
|
|
5
|
+
* @deprecated Use {@link PortableSource} instead, which wraps the Cloudflare `env` binding identically.
|
|
6
|
+
* Removed in v8. Replace `new WorkerEnvSource(env)` with `new PortableSource(env)`.
|
|
9
7
|
* @public
|
|
10
8
|
*/
|
|
11
|
-
declare class WorkerEnvSource
|
|
12
|
-
/** Always `false`. With no filesystem, the `.env` cascade and file APIs do not apply. */
|
|
13
|
-
readonly supportsFiles = false;
|
|
14
|
-
private readonly vars;
|
|
15
|
-
/** Seed the source from the Workers `env` binding. Non-string values are JSON-stringified, so they must be JSON-serializable. */
|
|
16
|
-
constructor(env: Record<string, unknown>);
|
|
17
|
-
/** Returns a snapshot clone of the `env` binding as plain strings. */
|
|
18
|
-
readVars(): Record<string, string>;
|
|
19
|
-
}
|
|
9
|
+
declare class WorkerEnvSource extends PortableSource {}
|
|
20
10
|
//#endregion
|
|
21
11
|
export { WorkerEnvSource };
|
|
22
12
|
//# sourceMappingURL=WorkerEnvSource.d.mts.map
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* A source with no filesystem: an injected object on the browser, or the Cloudflare `env` binding on
|
|
4
4
|
* Workers. The `.env` cascade, profiles, and the `envPaths`/`baseDir`/`configureProfiles` APIs do not
|
|
5
|
-
* apply
|
|
5
|
+
* apply. Only `readVars()` populates the cache.
|
|
6
6
|
* @public
|
|
7
7
|
*/
|
|
8
8
|
interface BareEnvSource {
|
|
9
9
|
/** Return every variable this source provides, as plain strings. */
|
|
10
10
|
readVars(): Record<string, string>;
|
|
11
|
-
/** Leave unset (or `false`)
|
|
11
|
+
/** Leave unset (or `false`) because a bare source has no filesystem. */
|
|
12
12
|
readonly supportsFiles?: false;
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
@@ -24,21 +24,26 @@ interface FileEnvSource {
|
|
|
24
24
|
readonly supportsFiles: true;
|
|
25
25
|
/** Read a file's text, or `undefined` when it is absent or unreadable. Backs the loader and existence checks. */
|
|
26
26
|
readFile(path: string, encoding: string): string | undefined;
|
|
27
|
-
/** Join a relative `.env` path onto `baseDir
|
|
27
|
+
/** Join a relative `.env` path onto `baseDir`. Absolute paths are returned unchanged. */
|
|
28
28
|
resolvePath(baseDir: string, candidate: string): string;
|
|
29
29
|
/** Normalize a `baseDir` value (a directory path, or a module / `file:` URL) to an absolute directory path. */
|
|
30
30
|
normalizeBaseDir(value: string | URL): string;
|
|
31
|
-
/** Mirror loaded keys back to the ambient environment (e.g. `process.env`)
|
|
31
|
+
/** Mirror loaded keys back to the ambient environment (e.g. `process.env`), backing `Envapter.syncProcessEnv`. */
|
|
32
32
|
writeVars(vars: Record<string, string>): void;
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
35
|
-
* A pluggable source of environment variables. The default {@link NodeEnvSource} (a
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
35
|
+
* A pluggable source of environment variables. The default {@link NodeEnvSource} (a {@link FileEnvSource})
|
|
36
|
+
* reads `process.env` plus the `.env` cascade. A {@link BareEnvSource} lets the same engine run where
|
|
37
|
+
* there is no ambient environment (an injected object on the browser, the Cloudflare `env` binding on
|
|
38
|
+
* Workers). Bind one with `Envapter.useSource`.
|
|
39
39
|
* @public
|
|
40
40
|
*/
|
|
41
|
-
type
|
|
41
|
+
type Source = BareEnvSource | FileEnvSource;
|
|
42
|
+
/**
|
|
43
|
+
* @public
|
|
44
|
+
* @deprecated Renamed to {@link Source} in v8.
|
|
45
|
+
*/
|
|
46
|
+
type EnvSource = Source;
|
|
42
47
|
//#endregion
|
|
43
|
-
export type { BareEnvSource, EnvSource, FileEnvSource };
|
|
48
|
+
export type { BareEnvSource, EnvSource, FileEnvSource, Source };
|
|
44
49
|
//# sourceMappingURL=Source.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EnvapterBase.mjs","names":[],"sources":["../../../src/core/EnvapterBase.ts"],"sourcesContent":["import { Validator } from '../engine/Validators';\nimport { debugVerbose, getDebugLevel, setDebugLevel } from '../infra/Debug';\nimport { loadDotenv } from '../infra/Dotenv';\nimport { EnvaptError, EnvaptErrorCodes } from '../infra/Error';\nimport { bindRuntimeFromSource } from '../infra/runtime';\nimport { UnboundEnvSource } from '../sources/UnboundEnvSource';\n\nimport type { DebugLevel } from '../infra/Debug';\nimport type { EnvFileOptions } from '../infra/Dotenv';\nimport type { EnvKeyInput, EnvSource, FileEnvSource } from '../types';\n\n/** @internal */\nexport const EnvaptCache = new Map<string, unknown>();\n\n/** @internal */\nexport abstract class EnvapterBase {\n protected static _envPaths: string[] = ['.env'];\n protected static _envPathsExplicitlySet = false;\n protected static _baseDir: string | undefined = undefined;\n protected static _userDefinedEnvFileOptions: EnvFileOptions = {};\n protected static _strict = false;\n protected static _syncProcessEnv = false;\n // Loader-written keys only (collisions skipped). Refilled on every cache rebuild.\n protected static _dotenvAddedKeys: Set<string> = new Set<string>();\n // Unbound by default so non-Node builds throw NoSourceBound on read until useSource() is called.\n // NodeEnvapter's static block binds NodeEnvSource when referenced, so `import 'envapt'` needs no setup.\n protected static _source: EnvSource = new UnboundEnvSource();\n\n /**\n * Enable or disable strict mode. Default `false`. Setting refreshes the cache so\n * previously-cached converted values get re-evaluated under the new rule.\n */\n static set strict(value: boolean) {\n // Anchored to EnvapterBase: `this._strict` would write an own-property on the subclass that base readers miss.\n EnvapterBase._strict = value;\n // `this`, not EnvapterBase: rebuild via the subclass so its `resolveEffectivePaths` override is honored.\n this.refreshCache();\n }\n\n static get strict(): boolean {\n return EnvapterBase._strict;\n }\n\n /**\n * Set the debug log level. Defaults to `silent`. When unset, reads `ENVAPT_DEBUG` from the\n * bound source on first access; the setter overrides any env-var value. Output goes to stderr\n * on Node (the console elsewhere), prefixed with `[envapt]`.\n */\n static set debug(level: DebugLevel) {\n setDebugLevel(level);\n }\n\n static get debug(): DebugLevel {\n return getDebugLevel();\n }\n\n /**\n * Opt-in mirror of dotenv-loaded keys back to `process.env`. Default `false`.\n *\n * Only keys the loader actually wrote are mirrored, so collision behavior follows\n * `envFileOptions.override`: with the default `false`, pre-existing `process.env` values\n * are preserved; with `true`, the file value wins in both the cache and the mirror.\n *\n * Flipping `false → true` mirrors the existing tracked delta immediately (no cache\n * refresh). Flipping `true → false` is one-way: previously mirrored keys remain in\n * `process.env` until the process exits.\n */\n static set syncProcessEnv(value: boolean) {\n Validator.validateSyncProcessEnv(value);\n const previous = EnvapterBase._syncProcessEnv;\n // Anchored to EnvapterBase: `this._syncProcessEnv` would write an own-property on the subclass that base readers miss.\n EnvapterBase._syncProcessEnv = value;\n if (!previous && value && EnvaptCache.size > 0) this.mirrorToProcessEnv();\n }\n\n static get syncProcessEnv(): boolean {\n return EnvapterBase._syncProcessEnv;\n }\n\n protected static treatAsMissing(value: string | undefined): boolean {\n if (value === undefined || value === '') return true;\n if (EnvapterBase._strict && value.trim() === '') return true;\n return false;\n }\n\n // No baseDir: candidate returned unchanged so the source resolves it against its own default\n // (process.cwd() on Node). Resolution goes through the source to keep this class node-free.\n protected static resolveAgainstBase(candidate: string): string {\n const baseDir = EnvapterBase._baseDir;\n if (baseDir === undefined) return candidate;\n const source = EnvapterBase._source;\n /* v8 ignore next -- @preserve callers are all file-gated, so the source is never bare here */\n if (!source.supportsFiles) return candidate;\n return source.resolvePath(baseDir, candidate);\n }\n\n // File-based config (envPaths/baseDir/configureProfiles) is meaningless without a filesystem;\n // throw instead of silently ignoring it on the browser or Workers. Narrows the source so callers\n // can reach the file capabilities (resolvePath/normalizeBaseDir) after the check.\n protected static assertFileApiSupported(api: string, source: EnvSource): asserts source is FileEnvSource {\n if (!source.supportsFiles) {\n throw new EnvaptError(\n EnvaptErrorCodes.FileApiUnsupported,\n `${api} requires a filesystem-backed source; the bound source does not support .env files.`\n );\n }\n }\n\n // Existence via the bound source instead of fs.existsSync/accessSync: a file \"exists\" when the\n // source can read it.\n protected static sourceFileExists(path: string): boolean {\n const source = EnvapterBase._source;\n /* v8 ignore next -- @preserve every caller is file-gated, so this never sees a bare source */\n if (!source.supportsFiles) return false;\n return source.readFile(path, 'utf8') !== undefined;\n }\n\n protected static refreshCache(): void {\n EnvaptCache.clear();\n EnvapterBase._dotenvAddedKeys = new Set();\n debugVerbose('cache cleared, reloading config');\n void this.config; // getter rebuilds the cache as a side effect\n }\n\n protected static mirrorToProcessEnv(): void {\n if (EnvapterBase._dotenvAddedKeys.size === 0) return;\n const source = EnvapterBase._source;\n /* v8 ignore next -- @preserve dotenv keys only accumulate under a file source, so the delta implies supportsFiles here */\n if (!source.supportsFiles) return;\n const mirrored: Record<string, string> = {};\n for (const key of EnvapterBase._dotenvAddedKeys) {\n const value = EnvaptCache.get(key);\n /* v8 ignore next -- @preserve loader only writes strings; defensive against future cache contents */\n if (typeof value !== 'string') continue;\n mirrored[key] = this.resolveForMirror(key, value);\n debugVerbose(`mirrored ${key} to the ambient environment`);\n }\n source.writeVars(mirrored);\n debugVerbose(`mirrored ${EnvapterBase._dotenvAddedKeys.size} keys to the ambient environment`);\n }\n\n // The template resolver is defined in PrimitiveMethods, and EnvapterBase can't call it without an\n // import cycle, so the mirror expands ${VAR} through this override.\n protected static resolveForMirror(_key: string, value: string): string {\n /* v8 ignore next -- @preserve overridden by PrimitiveMethods on every concrete class */\n return value;\n }\n\n // Default returns the explicit `_envPaths`; EnvironmentMethods overrides to layer the dotenv-flow\n // cascade + configureProfiles when envPaths was never explicitly set.\n protected static resolveEffectivePaths(): string[] {\n /* v8 ignore next -- @preserve */\n return this._envPaths.map((p) => this.resolveAgainstBase(p));\n }\n\n protected static resolveKeyInput(keyInput: EnvKeyInput): { key: string; value: string | undefined } {\n const keys = Array.isArray(keyInput) ? keyInput : [keyInput];\n const normalizedKeys = keys as readonly string[];\n\n if (normalizedKeys.length === 0) {\n throw new EnvaptError(EnvaptErrorCodes.InvalidKeyInput, 'At least one environment key must be provided.');\n }\n\n if (normalizedKeys.some((k) => typeof k !== 'string')) {\n throw new EnvaptError(EnvaptErrorCodes.InvalidKeyInput, 'Environment keys must be strings.');\n }\n\n if (normalizedKeys.some((k) => k.trim() === '')) {\n throw new EnvaptError(EnvaptErrorCodes.InvalidKeyInput, 'Environment keys cannot be empty strings.');\n }\n\n for (const candidate of normalizedKeys) {\n const value = this.config.get(candidate) as string | undefined;\n if (value !== undefined) {\n return { key: candidate, value };\n }\n }\n\n return { key: normalizedKeys[0] as string, value: undefined };\n }\n\n protected static get config(): Map<string, unknown> {\n if (EnvaptCache.size === 0) {\n const source = EnvapterBase._source;\n // Clone so the loader and downstream reads never mutate the source's backing object.\n const isolatedEnv: Record<string, string> = { ...source.readVars() };\n\n let added = new Set<string>();\n // Sources without a filesystem (injected objects on the browser or Workers) skip the\n // .env cascade, profiles, and envPaths; only the readVars() snapshot populates the cache.\n if (source.supportsFiles) {\n debugVerbose(`base dir: ${EnvapterBase._baseDir ?? 'working directory'}`);\n // Outside the try below so a missing configured profile path surfaces its EnvaptError; only dotenv parse errors stay caught.\n const effectivePaths = this.resolveEffectivePaths();\n debugVerbose(\n `effective .env paths: ${effectivePaths.length === 0 ? '(none)' : effectivePaths.join(', ')}`\n );\n try {\n added = loadDotenv({\n ...this._userDefinedEnvFileOptions,\n path: effectivePaths,\n processEnv: isolatedEnv,\n readFile: source.readFile.bind(source)\n });\n } catch {}\n }\n EnvapterBase._dotenvAddedKeys = added;\n for (const [key, value] of Object.entries(isolatedEnv)) EnvaptCache.set(key, value);\n debugVerbose(`cache populated: ${EnvaptCache.size} keys total`);\n if (EnvapterBase._syncProcessEnv) this.mirrorToProcessEnv();\n }\n\n return EnvaptCache;\n }\n\n /**\n * Eagerly load the `.env` cascade now instead of lazily on the first read. Idempotent: a no-op\n * once the cache is built. Useful before mirroring to `process.env` (see {@link syncProcessEnv}),\n * which is what the `envapt/config` side-effect entry does.\n */\n static load(): void {\n void this.config;\n }\n\n /**\n * Bind the environment {@link EnvSource}. On Node the entry binds {@link NodeEnvSource} for you\n * (a `process.env` snapshot plus the `.env` cascade); on the browser or Workers, pass a\n * `ManualEnvSource` / `WorkerEnvSource` (or any `EnvSource`) before reading. Clears and rebuilds\n * the cache.\n */\n static useSource(source: EnvSource): void {\n EnvapterBase._source = source;\n bindRuntimeFromSource(source);\n this.refreshCache();\n }\n\n /**\n * Read an environment variable as its raw string, skipping parsing and conversion.\n */\n getRaw(key: EnvKeyInput): string | undefined {\n return EnvapterBase.resolveKeyInput(key).value;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EnvapterBase.mjs","names":[],"sources":["../../../src/core/EnvapterBase.ts"],"sourcesContent":["import { Validator } from '../engine/Validators';\nimport { debugVerbose, getDebugLevel, setDebugLevel } from '../infra/Debug';\nimport { loadDotenv } from '../infra/Dotenv';\nimport { EnvaptError, EnvaptErrorCodes } from '../infra/Error';\nimport { bindRuntimeFromSource } from '../infra/runtime';\nimport { UnboundEnvSource } from '../sources/UnboundEnvSource';\n\nimport type { DebugLevel } from '../infra/Debug';\nimport type { EnvFileOptions } from '../infra/Dotenv';\nimport type { EnvKeyInput, FileEnvSource, Source } from '../types';\n\n/** @internal */\nexport const EnvaptCache = new Map<string, unknown>();\n\n/** @internal */\nexport abstract class EnvapterBase {\n protected static _envPaths: string[] = ['.env'];\n protected static _envPathsExplicitlySet = false;\n protected static _baseDir: string | undefined = undefined;\n protected static _userDefinedEnvFileOptions: EnvFileOptions = {};\n protected static _strict = false;\n protected static _syncProcessEnv = false;\n // Loader-written keys only (collisions skipped). Refilled on every cache rebuild.\n protected static _dotenvAddedKeys: Set<string> = new Set<string>();\n // Unbound by default so non-Node builds throw NoSourceBound on read until useSource() is called.\n // NodeEnvapter's static block binds NodeEnvSource when referenced, so `import 'envapt'` needs no setup.\n protected static _source: Source = new UnboundEnvSource();\n\n /**\n * Enable or disable strict mode. Default `false`. Setting refreshes the cache so\n * previously-cached converted values get re-evaluated under the new rule.\n */\n static set strict(value: boolean) {\n // Anchored to EnvapterBase: `this._strict` would write an own-property on the subclass that base readers miss.\n EnvapterBase._strict = value;\n // rebuild via `this` so the subclass `resolveEffectivePaths` override is honored (EnvapterBase would skip it).\n this.refreshCache();\n }\n\n static get strict(): boolean {\n return EnvapterBase._strict;\n }\n\n /**\n * Set the debug log level. Defaults to `silent`. When unset, reads `ENVAPT_DEBUG` from the\n * bound source on first access. The setter overrides any env-var value. Output goes to stderr\n * on Node (the console elsewhere), prefixed with `[envapt]`.\n */\n static set debug(level: DebugLevel) {\n setDebugLevel(level);\n }\n\n static get debug(): DebugLevel {\n return getDebugLevel();\n }\n\n /**\n * Opt-in mirror of dotenv-loaded keys back to `process.env`. Default `false`.\n *\n * Only keys the loader actually wrote are mirrored, so collision behavior follows\n * `envFileOptions.override`: with the default `false`, pre-existing `process.env` values\n * are preserved. With `true`, the file value wins in both the cache and the mirror.\n *\n * Flipping `false → true` mirrors the existing tracked delta immediately (no cache\n * refresh). Flipping `true → false` is one-way: previously mirrored keys remain in\n * `process.env` until the process exits.\n */\n static set syncProcessEnv(value: boolean) {\n Validator.validateSyncProcessEnv(value);\n const previous = EnvapterBase._syncProcessEnv;\n // Anchored to EnvapterBase: `this._syncProcessEnv` would write an own-property on the subclass that base readers miss.\n EnvapterBase._syncProcessEnv = value;\n if (!previous && value && EnvaptCache.size > 0) this.mirrorToProcessEnv();\n }\n\n static get syncProcessEnv(): boolean {\n return EnvapterBase._syncProcessEnv;\n }\n\n protected static treatAsMissing(value: string | undefined): boolean {\n if (value === undefined || value === '') return true;\n if (EnvapterBase._strict && value.trim() === '') return true;\n return false;\n }\n\n // No baseDir: candidate returned unchanged so the source resolves it against its own default\n // (process.cwd() on Node). Resolution goes through the source to keep this class node-free.\n protected static resolveAgainstBase(candidate: string): string {\n const baseDir = EnvapterBase._baseDir;\n if (baseDir === undefined) return candidate;\n const source = EnvapterBase._source;\n /* v8 ignore next -- @preserve callers are all file-gated, so the source is never bare here */\n if (!source.supportsFiles) return candidate;\n return source.resolvePath(baseDir, candidate);\n }\n\n // File-based config (envPaths/baseDir/configureProfiles) is meaningless without a filesystem, and\n // it throws instead of silently ignoring it on the browser or Workers. Narrows the source so callers\n // can reach the file capabilities (resolvePath/normalizeBaseDir) after the check.\n protected static assertFileApiSupported(api: string, source: Source): asserts source is FileEnvSource {\n if (!source.supportsFiles) {\n throw new EnvaptError(\n EnvaptErrorCodes.FileApiUnsupported,\n `${api} requires a filesystem-backed source; the bound source does not support .env files.`\n );\n }\n }\n\n // Existence via the bound source instead of fs.existsSync/accessSync: a file \"exists\" when the\n // source can read it.\n protected static sourceFileExists(path: string): boolean {\n const source = EnvapterBase._source;\n /* v8 ignore next -- @preserve every caller is file-gated, so this never sees a bare source */\n if (!source.supportsFiles) return false;\n return source.readFile(path, 'utf8') !== undefined;\n }\n\n protected static refreshCache(): void {\n EnvaptCache.clear();\n EnvapterBase._dotenvAddedKeys = new Set();\n debugVerbose('cache cleared, reloading config');\n void this.config; // getter rebuilds the cache as a side effect\n }\n\n protected static mirrorToProcessEnv(): void {\n if (EnvapterBase._dotenvAddedKeys.size === 0) return;\n const source = EnvapterBase._source;\n /* v8 ignore next -- @preserve dotenv keys only accumulate under a file source, so the delta implies supportsFiles here */\n if (!source.supportsFiles) return;\n const mirrored: Record<string, string> = {};\n for (const key of EnvapterBase._dotenvAddedKeys) {\n const value = EnvaptCache.get(key);\n /* v8 ignore next -- @preserve loader only writes strings, defensive against future cache contents */\n if (typeof value !== 'string') continue;\n mirrored[key] = this.resolveForMirror(key, value);\n debugVerbose(`mirrored ${key} to the ambient environment`);\n }\n source.writeVars(mirrored);\n debugVerbose(`mirrored ${EnvapterBase._dotenvAddedKeys.size} keys to the ambient environment`);\n }\n\n // The template resolver is defined in PrimitiveMethods, and EnvapterBase can't call it without an\n // import cycle, so the mirror expands ${VAR} through this override.\n protected static resolveForMirror(_key: string, value: string): string {\n /* v8 ignore next -- @preserve overridden by PrimitiveMethods on every concrete class */\n return value;\n }\n\n // Default returns the explicit `_envPaths`. EnvironmentMethods overrides to layer the dotenv-flow\n // cascade + configureProfiles when envPaths was never explicitly set.\n protected static resolveEffectivePaths(): string[] {\n /* v8 ignore next -- @preserve */\n return this._envPaths.map((p) => this.resolveAgainstBase(p));\n }\n\n protected static resolveKeyInput(keyInput: EnvKeyInput): { key: string; value: string | undefined } {\n const keys = Array.isArray(keyInput) ? keyInput : [keyInput];\n const normalizedKeys = keys as readonly string[];\n\n if (normalizedKeys.length === 0) {\n throw new EnvaptError(EnvaptErrorCodes.InvalidKeyInput, 'At least one environment key must be provided.');\n }\n\n if (normalizedKeys.some((k) => typeof k !== 'string')) {\n throw new EnvaptError(EnvaptErrorCodes.InvalidKeyInput, 'Environment keys must be strings.');\n }\n\n if (normalizedKeys.some((k) => k.trim() === '')) {\n throw new EnvaptError(EnvaptErrorCodes.InvalidKeyInput, 'Environment keys cannot be empty strings.');\n }\n\n for (const candidate of normalizedKeys) {\n const value = this.config.get(candidate) as string | undefined;\n if (value !== undefined) {\n return { key: candidate, value };\n }\n }\n\n return { key: normalizedKeys[0] as string, value: undefined };\n }\n\n protected static get config(): Map<string, unknown> {\n if (EnvaptCache.size === 0) {\n const source = EnvapterBase._source;\n // Clone so the loader and downstream reads never mutate the source's backing object.\n const isolatedEnv: Record<string, string> = { ...source.readVars() };\n\n let added = new Set<string>();\n // Sources without a filesystem (injected objects on the browser or Workers) skip the\n // .env cascade, profiles, and envPaths. Only the readVars() snapshot populates the cache.\n if (source.supportsFiles) {\n debugVerbose(`base dir: ${EnvapterBase._baseDir ?? 'working directory'}`);\n // Outside the try below so a missing configured profile path surfaces its EnvaptError. Only dotenv parse errors stay caught.\n const effectivePaths = this.resolveEffectivePaths();\n debugVerbose(\n `effective .env paths: ${effectivePaths.length === 0 ? '(none)' : effectivePaths.join(', ')}`\n );\n try {\n added = loadDotenv({\n ...this._userDefinedEnvFileOptions,\n path: effectivePaths,\n processEnv: isolatedEnv,\n readFile: source.readFile.bind(source)\n });\n } catch {}\n }\n EnvapterBase._dotenvAddedKeys = added;\n for (const [key, value] of Object.entries(isolatedEnv)) EnvaptCache.set(key, value);\n debugVerbose(`cache populated: ${EnvaptCache.size} keys total`);\n if (EnvapterBase._syncProcessEnv) this.mirrorToProcessEnv();\n }\n\n return EnvaptCache;\n }\n\n /**\n * Eagerly load the `.env` cascade now instead of lazily on the first read. Idempotent: a no-op\n * once the cache is built. Useful before mirroring to `process.env` (see {@link syncProcessEnv}),\n * which is what the `envapt/config` side-effect entry does.\n */\n static load(): void {\n void this.config;\n }\n\n /**\n * Bind the environment {@link Source}. On Node the entry binds {@link NodeEnvSource} for you\n * (a `process.env` snapshot plus the `.env` cascade). On the browser or Workers, pass a\n * `PortableSource` (or any `Source`) before reading. Clears and rebuilds the cache.\n */\n static useSource(source: Source): void {\n EnvapterBase._source = source;\n bindRuntimeFromSource(source);\n this.refreshCache();\n }\n\n /**\n * Read an environment variable as its raw string, skipping parsing and conversion.\n */\n getRaw(key: EnvKeyInput): string | undefined {\n return EnvapterBase.resolveKeyInput(key).value;\n }\n}\n"],"mappings":"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"}
|
package/dist/workerd/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Converters as e,isArrayOf as t}from"./converters/Converters.mjs";import{EnvaptError as n,EnvaptErrorCodes as r}from"./infra/Error.mjs";import{Environment as i}from"./core/EnvironmentMethods.mjs";import{
|
|
1
|
+
import{Converters as e,isArrayOf as t}from"./converters/Converters.mjs";import{EnvaptError as n,EnvaptErrorCodes as r}from"./infra/Error.mjs";import{Environment as i}from"./core/EnvironmentMethods.mjs";import{PortableSource as a}from"./sources/PortableSource.mjs";import{ManualEnvSource as o}from"./sources/ManualEnvSource.mjs";import{WorkerEnvSource as s}from"./sources/WorkerEnvSource.mjs";import{Envapt as c}from"./decorators/modern/Envapt.mjs";import{EnvBool as l,EnvNum as u,EnvStr as d,EnvTime as f,EnvUrl as p}from"./decorators/modern/SugarDecorators.mjs";import{PortableEnvapter as m}from"./engine/PortableEnvapter.mjs";export{e as Converters,l as EnvBool,u as EnvNum,d as EnvStr,f as EnvTime,p as EnvUrl,c as Envapt,n as EnvaptError,r as EnvaptErrorCodes,m as Envapter,i as Environment,o as ManualEnvSource,a as PortableSource,s as WorkerEnvSource,t as isArrayOf};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.mjs","names":[],"sources":["../../../src/infra/runtime.ts"],"sourcesContent":["import type {
|
|
1
|
+
{"version":3,"file":"runtime.mjs","names":[],"sources":["../../../src/infra/runtime.ts"],"sourcesContent":["import type { Source } from '../types';\n\n// importing Debug back into EnvapterBase would cycle, so the source and sink are injected via the setters below.\n/* v8 ignore start -- @preserve replaced at load on Node, so the Node suite never runs these defaults */\nlet sink: (line: string) => void = (line) => {\n // eslint-disable-next-line no-console -- the off-Node fallback log sink\n console.error(line);\n};\nlet envReader: () => Record<string, string> = () => ({});\n/* v8 ignore stop */\n\nexport function setRuntimeSink(fn: (line: string) => void): void {\n sink = fn;\n}\n\nexport function writeRuntimeLine(line: string): void {\n sink(line);\n}\n\nexport function readRuntimeEnv(): Record<string, string> {\n return envReader();\n}\n\nexport function bindRuntimeFromSource(source: Source): void {\n envReader = (): Record<string, string> => source.readVars();\n}\n"],"mappings":"AAIA,IAAI,EAAgC,GAAS,CAEzC,QAAQ,MAAM,CAAI,CACtB,EACI,OAAiD,CAAC,GAOtD,SAAgB,EAAiB,EAAoB,CACjD,EAAK,CAAI,CACb,CAEA,SAAgB,GAAyC,CACrD,OAAO,EAAU,CACrB,CAEA,SAAgB,EAAsB,EAAsB,CACxD,MAA0C,EAAO,SAAS,CAC9D"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{PortableSource as e}from"./PortableSource.mjs";var t=class extends e{};export{t as ManualEnvSource};
|
|
2
2
|
//# sourceMappingURL=ManualEnvSource.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ManualEnvSource.mjs","names":[],"sources":["../../../src/sources/ManualEnvSource.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"ManualEnvSource.mjs","names":[],"sources":["../../../src/sources/ManualEnvSource.ts"],"sourcesContent":["import { PortableSource } from './PortableSource';\n\n/**\n * @deprecated Renamed to {@link PortableSource}, which serves every no-filesystem runtime the same way.\n * Removed in v8. Replace `new ManualEnvSource(obj)` with `new PortableSource(obj)`.\n * @public\n */\nexport class ManualEnvSource extends PortableSource {}\n"],"mappings":"sDAOA,IAAa,EAAb,cAAqC,CAAe,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PortableSource.mjs","names":[],"sources":["../../../src/sources/PortableSource.ts"],"sourcesContent":["import { coerceToStringRecord } from './coerce';\n\nimport type { BareEnvSource } from '../types';\n\n/**\n * Environment source seeded from an object you provide, for every runtime without a filesystem. The\n * config your bundler injects into a browser build (Vite's `import.meta.env`, a webpack `DefinePlugin`\n * global), the Cloudflare `env` binding, a Vercel Edge or Fastly config object, a test fixture, or any\n * plain object. Pass it straight through. Non-string values are JSON-stringified so the converters\n * still apply, which means they must be JSON-serializable. Without a filesystem the `.env` cascade and\n * file APIs do not apply.\n * @public\n */\nexport class PortableSource implements BareEnvSource {\n /** Always `false`. With no filesystem, the `.env` cascade and file APIs do not apply. */\n readonly supportsFiles = false;\n private readonly vars: Record<string, string>;\n\n /** Seed the source from `vars`. Non-string values are JSON-stringified so the converters still apply. */\n constructor(vars: object) {\n this.vars = coerceToStringRecord(vars);\n }\n\n /** Returns a snapshot clone of the provided object as plain strings. */\n readVars(): Record<string, string> {\n return { ...this.vars };\n }\n}\n"],"mappings":"oDAaA,IAAa,EAAb,KAAqD,CAEjD,cAAyB,GACzB,KAGA,YAAY,EAAc,CACtB,KAAK,KAAO,EAAqB,CAAI,CACzC,CAGA,UAAmC,CAC/B,MAAO,CAAE,GAAG,KAAK,IAAK,CAC1B,CACJ"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{EnvaptError as e}from"../infra/Error.mjs";var t=class{supportsFiles=!1;readVars(){throw new e(307,`No environment source is bound. Call Envapter.useSource(...) with a source such as new
|
|
1
|
+
import{EnvaptError as e}from"../infra/Error.mjs";var t=class{supportsFiles=!1;readVars(){throw new e(307,`No environment source is bound. Call Envapter.useSource(...) with a source such as new PortableSource({...}) before reading.`)}};export{t as UnboundEnvSource};
|
|
2
2
|
//# sourceMappingURL=UnboundEnvSource.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UnboundEnvSource.mjs","names":[],"sources":["../../../src/sources/UnboundEnvSource.ts"],"sourcesContent":["import { EnvaptError, EnvaptErrorCodes } from '../infra/Error';\n\nimport type { BareEnvSource } from '../types';\n\n/**\n * The default source until one is bound. Every read throws NoSourceBound so a forgotten `useSource`\n * surfaces at read time instead of silently yielding nothing. The Node entry binds {@link NodeEnvSource}\n * at load
|
|
1
|
+
{"version":3,"file":"UnboundEnvSource.mjs","names":[],"sources":["../../../src/sources/UnboundEnvSource.ts"],"sourcesContent":["import { EnvaptError, EnvaptErrorCodes } from '../infra/Error';\n\nimport type { BareEnvSource } from '../types';\n\n/**\n * The default source until one is bound. Every read throws NoSourceBound so a forgotten `useSource`\n * surfaces at read time instead of silently yielding nothing. The Node entry binds {@link NodeEnvSource}\n * at load, browser and Workers bundles must call `Envapter.useSource(...)` before reading.\n * @internal\n */\nexport class UnboundEnvSource implements BareEnvSource {\n readonly supportsFiles = false;\n\n readVars(): Record<string, string> {\n throw new EnvaptError(\n EnvaptErrorCodes.NoSourceBound,\n 'No environment source is bound. Call Envapter.useSource(...) with a source such as new PortableSource({...}) before reading.'\n );\n }\n}\n"],"mappings":"iDAUA,IAAa,EAAb,KAAuD,CACnD,cAAyB,GAEzB,UAAmC,CAC/B,MAAM,IAAI,EAAA,IAEN,8HACJ,CACJ,CACJ"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{PortableSource as e}from"./PortableSource.mjs";var t=class extends e{};export{t as WorkerEnvSource};
|
|
2
2
|
//# sourceMappingURL=WorkerEnvSource.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkerEnvSource.mjs","names":[],"sources":["../../../src/sources/WorkerEnvSource.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"WorkerEnvSource.mjs","names":[],"sources":["../../../src/sources/WorkerEnvSource.ts"],"sourcesContent":["import { PortableSource } from './PortableSource';\n\n/**\n * @deprecated Use {@link PortableSource} instead, which wraps the Cloudflare `env` binding identically.\n * Removed in v8. Replace `new WorkerEnvSource(env)` with `new PortableSource(env)`.\n * @public\n */\nexport class WorkerEnvSource extends PortableSource {}\n"],"mappings":"sDAOA,IAAa,EAAb,cAAqC,CAAe,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coerce.mjs","names":[],"sources":["../../../src/sources/coerce.ts"],"sourcesContent":["// Shared by
|
|
1
|
+
{"version":3,"file":"coerce.mjs","names":[],"sources":["../../../src/sources/coerce.ts"],"sourcesContent":["// Shared by PortableSource and its deprecated aliases. The returned record is fresh, so a caller\n// mutating its object later cannot leak into the source, and non-string values are JSON-stringified.\nexport function coerceToStringRecord(env: object): Record<string, string> {\n const snapshot: Record<string, string> = {};\n // `object` so a Cloudflare `Env` (an interface with no index signature) is accepted. cast to a record\n // to read entries as `unknown` rather than `any`.\n for (const [key, value] of Object.entries(env as Record<string, unknown>)) {\n if (typeof value === 'string') {\n snapshot[key] = value;\n continue;\n }\n const encoded = JSON.stringify(value);\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- TS lib mistypes JSON.stringify's return as always-string; it is string | undefined at runtime\n if (encoded !== undefined) snapshot[key] = encoded;\n }\n return snapshot;\n}\n"],"mappings":"AAEA,SAAgB,EAAqB,EAAqC,CACtE,IAAM,EAAmC,CAAC,EAG1C,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,CAA8B,EAAG,CACvE,GAAI,OAAO,GAAU,SAAU,CAC3B,EAAS,GAAO,EAChB,QACJ,CACA,IAAM,EAAU,KAAK,UAAU,CAAK,EAEhC,IAAY,IAAA,KAAW,EAAS,GAAO,EAC/C,CACA,OAAO,CACX"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "envapt",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "7.0
|
|
4
|
+
"version": "7.1.0",
|
|
5
5
|
"description": "Type-safe config for TypeScript. Read typed values from any source, process.env, .env files, Cloudflare Workers bindings, browser bundles, or any object you supply. Zero runtime dependencies, one API across Node, Bun, Deno, Workers, and the browser. TC39 accessor decorators (legacy decorators at envapt/legacy), converters, and Standard Schema (zod/valibot/arktype) validation.",
|
|
6
6
|
"types": "./dist/types/index.d.mts",
|
|
7
7
|
"exports": {
|
|
@@ -185,5 +185,5 @@
|
|
|
185
185
|
"bump": "pnpm tsx ../../scripts/release/bump-jsr.ts",
|
|
186
186
|
"release": "pnpm run cs:publish"
|
|
187
187
|
},
|
|
188
|
-
"readme": "<img src=\"https://raw.githubusercontent.com/materwelonDhruv/envapt/main/.github/assets/logo.png\" width=\"120\" align=\"left\" alt=\"envapt logo\" />\n\n<h3>envapt</h3>\n\n<p>\n <strong>The apt way to read typed config.</strong><br/>\n Read config from any source as real typed values, with zero runtime dependencies.\n</p>\n\n<p>\n <a href=\"https://www.npmjs.com/package/envapt\"><img alt=\"npm\" src=\"https://img.shields.io/npm/v/envapt?logo=npm&logoColor=cb3838&label=%20&labelColor=103544&color=cb3838\"></a>\n <a href=\"https://www.npmjs.com/package/envapt\"><img alt=\"downloads\" src=\"https://img.shields.io/npm/dm/envapt?style=flat&color=f7f6e8&labelColor=103544&label=downloads\"></a>\n <a href=\"https://jsr.io/@materwelon/envapt\"><img alt=\"jsr\" src=\"https://jsr.io/badges/@materwelon/envapt\"></a>\n <img alt=\"CI\" src=\"https://img.shields.io/github/actions/workflow/status/materwelonDhruv/envapt/checks.yml?branch=main&label=tests&style=flat&logo=github&color=3fb950&labelColor=103544\">\n <a href=\"LICENSE\"><img alt=\"License\" src=\"https://img.shields.io/npm/l/envapt?style=flat&color=e97826&logo=apache&label=\"></a>\n</p>\n\n<br clear=\"left\"/>\n\nenvapt returns config as the type you asked for instead of the `string | undefined` you get raw, with\na fallback that removes `undefined` from the return type. It reads from whatever source you bind. On\nNode, Bun, and Deno that is `process.env` and your `.env` files, bound on import. On Cloudflare\nWorkers, in the browser, or for a secrets object you fetched at boot, you bind the source with\n`Envapter.useSource(...)`.\n\n```ts\nimport { Envapter } from 'envapt';\n\nconst port = Envapter.getNumber('PORT', 3000); // number, not string | undefined\n```\n\n**[Read the docs →](https://envapt.materwelon.dev)**\n\n## What you get\n\n- **Typed values.** A fallback removes `undefined` from the return type. Built-in converters cover\n numbers, booleans, bigint, JSON, URLs, regular expressions, dates, durations, and arrays, or pass\n your own function or a Standard Schema validator (zod, valibot, arktype).\n- **Any source.** A source is any object with a `readVars()` method, so you can bind `process.env`, a\n Cloudflare Workers binding, a browser bundle, or a secrets payload you fetched from a store at boot.\n On Node, Bun, and Deno one binds on import.\n- **Zero runtime dependencies.** The reader, converters, and built-in `.env` parser are self-contained,\n so nothing is added to your dependency tree.\n- **Runs on Node, Bun, Deno, Cloudflare Workers, and the browser.** Node `>=20`, Bun `>=1.3`, Deno\n `>=2.5` (ESM and CJS). The Workers and browser builds resolve through the package `exports`\n conditions.\n- **`.env` loading built in on Node.** The default Node source adds a per-environment file cascade,\n `${VAR}` templates, and strict / required checks. Off Node there is no filesystem, so you bind\n another source with `Envapter.useSource(...)` and read with the same typed API.\n\n## Install\n\n```sh\nnpm install envapt\npnpm add envapt\nyarn add envapt\nbun add envapt\ndeno add jsr:@materwelon/envapt\n```\n\n## Quick start\n\nRead values functionally with `Envapter`, or bind them to class fields with the `@Envapt` decorator.\nBoth share the same parsing, converters, and cache.\n\n### Functional\n\nRead a value from any call site, in JavaScript or TypeScript. No build step. On Node the source is\nbound for you. On Workers and in the browser, call `Envapter.useSource(...)` first.\n\n```ts\nimport { Envapter, Converters } from 'envapt';\n\nconst port = Envapter.getNumber('PORT', 3000);\nconst origins = Envapter.getUsing('ALLOWED_ORIGINS', Converters.array(), []);\n```\n\nOn Cloudflare Workers, `env` is importable at module scope, so bind it once in a config module
|
|
188
|
+
"readme": "<img src=\"https://raw.githubusercontent.com/materwelonDhruv/envapt/main/.github/assets/logo.png\" width=\"120\" align=\"left\" alt=\"envapt logo\" />\n\n<h3>envapt</h3>\n\n<p>\n <strong>The apt way to read typed config.</strong><br/>\n Read config from any source as real typed values, with zero runtime dependencies.\n</p>\n\n<p>\n <a href=\"https://www.npmjs.com/package/envapt\"><img alt=\"npm\" src=\"https://img.shields.io/npm/v/envapt?logo=npm&logoColor=cb3838&label=%20&labelColor=103544&color=cb3838\"></a>\n <a href=\"https://www.npmjs.com/package/envapt\"><img alt=\"downloads\" src=\"https://img.shields.io/npm/dm/envapt?style=flat&color=f7f6e8&labelColor=103544&label=downloads\"></a>\n <a href=\"https://jsr.io/@materwelon/envapt\"><img alt=\"jsr\" src=\"https://jsr.io/badges/@materwelon/envapt\"></a>\n <img alt=\"CI\" src=\"https://img.shields.io/github/actions/workflow/status/materwelonDhruv/envapt/checks.yml?branch=main&label=tests&style=flat&logo=github&color=3fb950&labelColor=103544\">\n <a href=\"LICENSE\"><img alt=\"License\" src=\"https://img.shields.io/npm/l/envapt?style=flat&color=e97826&logo=apache&label=\"></a>\n</p>\n\n<br clear=\"left\"/>\n\nenvapt returns config as the type you asked for instead of the `string | undefined` you get raw, with\na fallback that removes `undefined` from the return type. It reads from whatever source you bind. On\nNode, Bun, and Deno that is `process.env` and your `.env` files, bound on import. On Cloudflare\nWorkers, in the browser, or for a secrets object you fetched at boot, you bind the source with\n`Envapter.useSource(...)`.\n\n```ts\nimport { Envapter } from 'envapt';\n\nconst port = Envapter.getNumber('PORT', 3000); // number, not string | undefined\n```\n\n**[Read the docs →](https://envapt.materwelon.dev)**\n\n## What you get\n\n- **Typed values.** A fallback removes `undefined` from the return type. Built-in converters cover\n numbers, booleans, bigint, JSON, URLs, regular expressions, dates, durations, and arrays, or pass\n your own function or a Standard Schema validator (zod, valibot, arktype).\n- **Any source.** A source is any object with a `readVars()` method, so you can bind `process.env`, a\n Cloudflare Workers binding, a browser bundle, or a secrets payload you fetched from a store at boot.\n On Node, Bun, and Deno one binds on import.\n- **Zero runtime dependencies.** The reader, converters, and built-in `.env` parser are self-contained,\n so nothing is added to your dependency tree.\n- **Runs on Node, Bun, Deno, Cloudflare Workers, and the browser.** Node `>=20`, Bun `>=1.3`, Deno\n `>=2.5` (ESM and CJS). The Workers and browser builds resolve through the package `exports`\n conditions.\n- **`.env` loading built in on Node.** The default Node source adds a per-environment file cascade,\n `${VAR}` templates, and strict / required checks. Off Node there is no filesystem, so you bind\n another source with `Envapter.useSource(...)` and read with the same typed API.\n\n## Install\n\n```sh\nnpm install envapt\npnpm add envapt\nyarn add envapt\nbun add envapt\ndeno add jsr:@materwelon/envapt\n```\n\n## Quick start\n\nRead values functionally with `Envapter`, or bind them to class fields with the `@Envapt` decorator.\nBoth share the same parsing, converters, and cache.\n\n### Functional\n\nRead a value from any call site, in JavaScript or TypeScript. No build step. On Node the source is\nbound for you. On Workers and in the browser, call `Envapter.useSource(...)` first.\n\n```ts\nimport { Envapter, Converters } from 'envapt';\n\nconst port = Envapter.getNumber('PORT', 3000);\nconst origins = Envapter.getUsing('ALLOWED_ORIGINS', Converters.array(), []);\n```\n\nOn Cloudflare Workers, `env` is importable at module scope, so bind it once in a config module, and in\nthe browser seed a `PortableSource` from the object your bundler injects.\n\n```ts\nimport { env } from 'cloudflare:workers';\nimport { Envapter, PortableSource } from 'envapt';\n\nEnvapter.useSource(new PortableSource(env));\n\nexport const apiToken = Envapter.get('API_TOKEN');\n```\n\n### Decorator\n\nBind a value to a class field with a TC39 accessor decorator. No `experimentalDecorators` flag, and it runs on Bun and Deno from `.ts` directly.\n\n```ts\nimport { EnvNum } from 'envapt';\n\nclass Config {\n @EnvNum('PORT', 3000)\n static accessor port: number;\n}\n```\n\nThe legacy (experimentalDecorators) decorators are exported from `envapt/legacy`.\n\n## Agent skill\n\nInstall the envapt agent skill so AI coding tools use the correct API:\n\n```sh\nnpx skills add materwelonDhruv/envapt\n```\n\n---\n\n<p align=\"center\"><sub>Built by <a href=\"https://github.com/materwelondhruv\">@materwelonDhruv</a> · Apache 2.0</sub></p>\n"
|
|
189
189
|
}
|