@wolfstar/env-utilities
v2.3.1
Published
Functional utilities for reading and parsing environmental variables
Maintainers
Readme
@wolfstar/env-utilities
Functional utilities for reading and parsing environment variables.
Description
Functional utilities for reading and parsing environmental variables, based on Wolfstar's internal tools.
Installation
pnpm add @wolfstar/env-utilitiesNothing else is needed: .env* files are parsed with Node.js' own util.parseEnv (Node.js 20.12 or newer), the node
loader. dotenv and dotenv-expand are optional peer dependencies: install them if you want the dotenv loader, which
setup() then uses by itself.
pnpm add dotenv dotenv-expandThe loaders:
node: Node.js' parser, with$VAR,${VAR},${VAR:-default},${VAR:+alternate}and\$expanded the waydotenv-expanddoes. Used whendotenvis not installed.dotenv:dotenvanddotenv-expand. Used when both are installed.varlock: see below. Used when the project has a.env.schema.
Set loader or DOTENV_LOADER to choose one yourself. Asking for dotenv without the packages installed throws an Error
that names the missing one.
Usage
Setup
To setup @wolfstar/env-utilities, you use the setup function exported by the package:
import { setup } from '@wolfstar/env-utilities';
// Finds src/.env* and .env* from the current project automatically.
setup();Alternatively, if you do not need to provide any custom options you can import it as a side effect:
import '@wolfstar/env-utilities/setup';You can also pass a string or if you want to define other options, you may use EnvSetupOptions. Optionally, you may configure dotenv via environment variables:
DOTENV_DEBUG: configuresEnvSetupOptions.debug. If enabled, the library will log to help debug why certain keys or values are not being set as expected.DOTENV_ENCODING: configuresEnvSetupOptions.encoding. If set, it will specify the encoding of the files containing the environment variablesDOTENV_ENV: configuresEnvSetupOptions.env. If set, it will specify a custom environment ifNODE_ENVis not sufficient.DOTENV_PATH: configuresEnvSetupOptions.path. If set, it will specify a custom path to the file containing environment variables, useful for when they are located elsewhere.DOTENV_PREFIX: configuresEnvSetupOptions.prefix. If set, it will specify a required prefix for dotenv variables (e.g.APP_).
With the stars CLI
A bot built with the stars CLI (@wolfstar/cli, compatibility version 5) does not call setup() itself: the
env option of stars.config mirrors EnvSetupOptions, and stars calls setup() with it before any module of the
bot runs. Do not call it by hand as well. Options set in stars.config win over the DOTENV_* variables above, the
same way options passed to setup() do. See env in the
@wolfstar/http-framework README.
// stars.config.ts
export default defineConfig({
env: { prefix: 'BOT_' }
});What .env files can be used?
Every file below is searched first under src/, then at the project root. An explicit path or DOTENV_PATH
disables this discovery and uses that base path only.
.env: Default..env.local: Local overrides. This file is loaded for all environments except test..env.development,.env.test,.env.production: Environment-specific settings..env.development.local,.env.test.local,.env.production.local: Local overrides of environment-specific settings.
Files on the left have more priority than files on the right:
npm start:.env.development.local,.env.local,.env.development,.envnpm test:.env.test.local,.env.test,.env(note.env.localis missing)
Experimental: varlock support
@wolfstar/env-utilities can optionally delegate environment variable resolution to
varlock, a schema-based alternative to dotenv with built-in validation, type-safety, and
secret protection. Support for it is experimental.
To use it, first set up varlock in your project (npx varlock init) and install the optional varlock package.
setup() then picks it by itself when the project has a schema: a src/.env.schema (looked up first, like src/.env*),
a .env.schema at the project root, or a varlock.loadPath in package.json. An explicit path or DOTENV_PATH keeps
the dotenv loader, and the loader option and the DOTENV_LOADER environment variable always have the last word:
A project that already has a
.env.schemaandvarlockinstalled, but loaded its.env*files through dotenv, switches to varlock with this detection. Varlock does its own.env*discovery, so the values can differ: setloader: 'dotenv'to opt out.
import { setup } from '@wolfstar/env-utilities';
setup({ loader: 'varlock' }); // or { loader: 'dotenv' } to keep dotenv next to a schemaDOTENV_LOADER=varlockWhen loader: 'varlock' is set, varlock resolves your checked-in .env.schema (with its own .env* file discovery and
validation) by running varlock load --format json-full, and the result is injected into process.env:
pathandenvare passed to varlock as--pathand--env;encodingis ignored in favour of the schema.prefixstill applies to the returnedparsedmap, which holds exactly the variables the schema resolved (as strings,@internalitems left out), including those that equal a valueprocess.envalready held.- An invalid schema makes
setup()throw anErrorcarrying varlock's summary, the same way the dotenv loader throws on an unreadable file, instead of exiting the process. - Varlock's runtime redaction and leak detection are not part of the CLI. Add
import 'varlock/auto-load'to opt into them. pathandenvcannot contain whitespace (varlock's CLI helper splits its command on spaces), except thatpathmay be a directory with whitespace in its name.
Typing Environment Variables
To add new entries, you augment Env from @wolfstar/env-utilities/dist/lib/types using any of the following types:
BooleanString: can be parsed withenvParseBoolean.IntegerString: can be parsed withenvParseInteger.NumberString: can be parsed withenvParseNumber.string: can be parsed withenvParseString.ArrayString: can be parsed withenvParseArray. A plainstringkey is not accepted byenvParseArray, declare the key asArrayStringinstead.
The above 5 functions will throw an ReferenceError instance if a key is missing (unless a default is passed in the second parameter) as well as a TypeError instance if a key could not be parsed. The default value is returned as-is and is not validated.
An example of adding more keys is as it follows:
import type { BooleanString, IntegerString, NumberString } from '@wolfstar/env-utilities';
declare module '@wolfstar/env-utilities' {
interface Env {
// Accepts 'true' or 'false':
ENABLE_TELEMETRY: BooleanString;
// Accepts any integer, e.g. '10':
REFRESH_INTERVAL: IntegerString;
// Accepts any number, e.g. '1.5':
MINIMUM_SPEED: NumberString;
// Accepts any string:
APPLICATION_SECRET: string;
}
}With a varlock schema
With varlock, the .env.schema is the source of truth and @generateTsTypes writes
the typings (CoercedEnvSchema, EnvSchemaAsStrings, ...). Instead of declaring every variable a second time in Env,
derive it from the generated CoercedEnvSchema with EnvFromVarlock:
// env.d.ts is generated by varlock and exports `CoercedEnvSchema`
import type { CoercedEnvSchema } from './env';
import type { EnvFromVarlock } from '@wolfstar/env-utilities/varlock';
declare module '@wolfstar/env-utilities' {
interface Env extends EnvFromVarlock<CoercedEnvSchema> {}
}EnvFromVarlock is also exported from the package root. It maps:
booleantoBooleanString, soenvParseBooleanaccepts the key.numberto a string that is both anIntegerStringand aNumberString: the coerced type cannot tell an integer from a float (andportis a number), so bothenvParseIntegerandenvParseNumberaccept the key.envParseIntegerthrows at runtime when a decimal-capable key holds1.5: useenvParseNumberfor those.stringandenumto their string form, soenvParseStringaccepts the key. An enum whose values look like a number or a boolean ('1' | '2') is widened withstring, so it stays a string key.- optional keys to optional keys.
- anything else, like an
array, to a plainstring.
NODE_ENV is left out: Env keeps declaring it as 'test' | 'development' | 'production', so a schema with a narrower
NODE_ENV still compiles.
Varlock serializes an array with its own separator (, by default), while envParseArray splits on spaces and needs an
ArrayString key. Read such a variable through env (already an array, see below) or declare the key as ArrayString by hand.
env, varlock's already-coerced values
@wolfstar/env-utilities/varlock also exports env, the ENV object of varlock/env. When setup() loads a schema
with the varlock loader, env.REDIS_PORT is already a validated number and env.API_ENABLED a boolean, typed by
the file varlock generates, so nothing is parsed a second time:
import { env } from '@wolfstar/env-utilities/varlock';
env.REDIS_PORT; // numberReach for env in new code of a varlock project, and keep envParse* for the code that has not moved yet, for projects
without varlock, and for values read straight from process.env. Both work side by side, so a project can migrate one
call at a time. Importing @wolfstar/env-utilities/varlock requires the varlock package.
