@caterpillar-soft/viper
v0.1.1
Published
Minimal Viper-inspired configuration library for TypeScript, powered by Zod and JSON5
Readme
viper
Minimal Viper-inspired configuration library for TypeScript, powered by Zod and JSON5.
Configuration comes from multiple layered sources — defaults, config file, environment variables, and explicit overrides — unified into a single queryable registry with dot-notation key access.
Features
- Layered config precedence: override > env > config file > defaults
- Dot-notation key access (
database.host) - Case-insensitive keys
- JSON5 config files with atomic write
- Environment variable binding (explicit + automatic with prefix)
- Optional Zod schema validation
- Key aliases and sub-tree extraction
Install
pnpm add @caterpillar-soft/viperQuick Start
import { createViper } from '@caterpillar-soft/viper'
import { z } from 'zod'
const schema = z.object({
host: z.string(),
port: z.number(),
database: z.object({
url: z.string(),
}),
})
const v = createViper({ schema })
// Set defaults (lowest priority)
v.setDefaults({ host: 'localhost', port: 3000 })
// Read from config file
v.setConfigName('config') // file name without extension
v.setConfigType('json5') // file extension
v.addConfigPath('./config') // search directory
v.addConfigPath('.')
v.readInConfig()
// Bind environment variables
v.setEnvPrefix('APP')
v.automaticEnv() // APP_HOST, APP_PORT, APP_DATABASE_URL
v.bindEnv('database.url', 'DATABASE_URL') // explicit binding
// Override (highest priority)
v.set('port', 8080)
// Read values
v.get('host') // => 'localhost'
v.getNumber('port') // => 8080
v.getString('database.url') // from env or config
// Introspection
v.isSet('host') // => true
v.allKeys() // => ['database.url', 'host', 'port']
v.allSettings() // merged config object
// Sub-tree
const db = v.sub('database')
db?.get('url')
// Write config
await v.writeConfigAs('./config/config.json5')Config Precedence
When reading a key, sources are checked in this order:
- Override — values set via
v.set(key, value) - Environment — bound env vars or automatic env lookup
- Config file — parsed from JSON5 file
- Defaults — values set via
v.setDefault()/v.setDefaults()
API
Construction
import { Viper, createViper } from '@caterpillar-soft/viper'
const v = new Viper()
const v = new Viper({ schema, keyDelimiter: '.' })
const v = createViper({ schema })Defaults
setDefault(key, value)— set a single defaultsetDefaults(obj)— merge an object into defaults
Config File
setConfigFile(path)— set explicit config file pathsetConfigName(name)— file name without extension (default:"config")setConfigType(type)— file extension (default:"json5")addConfigPath(dir)— add a directory to searchreadInConfig()— find, read, parse, and validate config filemergeInConfig()— likereadInConfig()but merges into existing configmergeConfigMap(obj)— merge a plain object into config layerconfigFileUsed()— returns the discovered config file path
Write
writeConfig()— atomic write to discovered config pathwriteConfigAs(path)— atomic write to specific pathsafeWriteConfig()— write only if file doesn't existsafeWriteConfigAs(path)— write to path only if it doesn't exist
Overrides
set(key, value)— set an override (highest priority)
Getters
get<T>(key)— returnsT | undefinedgetString(key)— returnsstring(empty string if missing)getNumber(key)— returnsnumber(0 if missing or NaN)getBoolean(key)— returnsboolean(false if missing)getArray(key)— returnsunknown[](empty array if not an array)getObject(key)— returnsRecord<string, unknown>(empty object if not an object)
Environment Variables
setEnvPrefix(prefix)— prefix for auto-derived env var namesbindEnv(key, ...envVars)— bind key to specific env varsautomaticEnv()— enable automatic env lookup ({PREFIX}_{KEY}, dots → underscores)
Introspection
isSet(key)— check if a key has a value in any layerallKeys()— sorted list of all known keysallSettings()— merged config from all layers
Alias & Sub-tree
registerAlias(alias, key)— alias resolves to real key on readsub(key)— returns a newViperinstance scoped to a nested key
License
MIT
