npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

configorama

v1.3.1

Published

Variable support for configuration files

Readme

Configorama

npm version license types

Resolve dynamic config values from environment variables, CLI flags, files, git data, expressions, and custom sources. Works with YAML, JSON, TOML, INI, HCL, Markdown, JavaScript, and TypeScript.

npm install configorama
npx configorama config.yml --stage prod

Configorama is a framework-agnostic variable engine for configuration files. Use it to resolve a config at runtime, inspect missing values before resolution, audit risky references, draw dependency graphs, run an interactive setup flow, or emit requirements JSON for agents and automation.

TL;DR

Deployment configs usually pull values from several places: env vars, CLI flags, local files, generated JavaScript, git metadata, stage-specific maps, and secret stores. Most config parsers stop at parsing, while framework-specific variable systems tend to stay tied to that framework.

Configorama loads a config file, finds variable references, resolves them in dependency order, applies filters/functions, and returns a plain JavaScript object. It can also report what the config needs before resolution.

Common use cases:

| Need | Support | Example | |---|---|---| | Resolve values from many sources | Built-in env, option/opt, self, file, text, git, cron, eval, and if sources | ${env:API_KEY}, ${option:stage}, ${file(./secrets.yml)} | | Keep config portable | Runs outside any framework | Use the same resolver in a CLI, build script, deploy job, or app bootstrap | | Prompt for missing inputs | Interactive setup wizard with type-aware prompts and masked secrets | configorama setup config.yml | | Tell agents what to provide | Requirements JSON with schemaVersion, requirements[], and ask[] | configorama inspect config.yml --view requirements | | Inspect before resolving | Full inspection model plus focused requirements, audit, and graph views | configorama inspect config.yml | | Document variables near the config | help() plus comment annotations for descriptions, obtain hints, examples, groups, sensitivity, and deprecation warnings | # @from Stripe dashboard > Developers > API keys | | Enforce runtime constraints | Type filters and oneOf(...) validation | ${option:threads \| Number \| oneOf(1, 2, 4)} |

Quick Example

# config.yml
service: billing-api

# Deployment stage
stage: ${option:stage | oneOf("dev", "staging", "prod")}

secrets:
  # Stripe live secret key
  # @from Stripe dashboard > Developers > API keys
  # @example sk_live_...
  # @sensitive true
  # @group Payments
  stripeSecret: ${env:STRIPE_SECRET_KEY}

database:
  host: ${env:DB_HOST, "localhost"}
  port: ${env:DB_PORT, 5432 | Number}
  name: ${self:service}-${self:stage}
# Resolve the config
STRIPE_SECRET_KEY=sk_live_xxx npx configorama config.yml --stage prod

# Walk through missing variables interactively
npx configorama setup config.yml

# Print requirements for agents or automation
npx configorama inspect config.yml --view requirements

# Inspect requirements, dependency graph, and audit report together
npx configorama inspect config.yml

What We Added Recently

| Area | Added | |---|---| | Normalized requirements model | ConfigRequirements groups occurrences by variable, normalizes ${opt:...} and ${option:...} as variableType: "option", and tracks paths, defaults, types, allowed values, sensitivity, and conflicts. | | Unified inspection CLI | configorama inspect config.yml emits requirements, graph, and audit output without resolving missing values. Use --view requirements, --view audit, or --view graph for one slice. | | Requirements JSON | configorama inspect config.yml --view requirements, configorama requirements config.yml, and configorama config.yml --requirements emit schemaVersion: 1, summary, requirements[], and environment-aware ask[]. | | Safe inspection | inspect, audit, and graph run in safe mode by default. Use --unsafe to opt out or --safe-root <dir> to restrict file/text references. | | Agent-friendly CLI contract | configorama capabilities prints commands, aliases, formats, flags, error codes, and exit codes as JSON. | | Path extraction polish | Jq-style paths, --raw, and --copy make scalar extraction usable in scripts: configorama -r --copy config.yml .database.host. | | Conflict handling | Conflicting type/default/allowed-value/annotation metadata is deterministic in the wizard. Requirements serialization fails on conflicts so agents get a clean contract. | | Setup wizard migration | The wizard now consumes prompt descriptors derived from the requirements model, supports enum selects from oneOf, displays annotation details, and redacts sensitive values in setup summaries and setup stdout. | | oneOf(...) validation | Runtime filter for inline literal sets and resolved list variables, including type-filter-first behavior such as ${option:threads \| Number \| oneOf(1, 2, 4)}. | | More type filters | Array and Object filters now validate/coerce arrays, comma-separated lists, JSON/JSON5 arrays, and JSON/JSON5 objects. | | Option alias | ${option:name} is supported alongside the existing ${opt:name} shorthand. | | Comment metadata | Leading/inline comments become help fallback; structured tags add @description, @from, @example, @default, @sensitive, @group, and @deprecated. | | Structured CLI errors | Inspection commands default to JSON errors on stderr; --error-format human is available for terminal use. |

Key Features

  • Multiple file formats - yml, yaml, json, toml, ini, hcl (Terraform), TypeScript, JavaScript, markdown
  • Rich variable sources - env vars, CLI flags, file refs, git data, cron expressions, eval/if expressions
  • Async/sync function execution - Import and execute JavaScript/TypeScript files with argument passing
  • Self-referencing - Reference other values within the same config using dot notation
  • Custom variable sources - Pluggable architecture to add your own variable resolvers
  • Filters and functions - Transform, coerce, constrain, and combine values with built-in or custom operators
  • Inspection modes - Prompt humans interactively, generate requirements JSON, audit risky references, or output dependency graphs
  • Comment annotations - Keep human and agent metadata beside the config value it describes
  • Metadata extraction - Analyze configs without resolving missing values, or get full resolution history
  • Circular dependency detection - Helpful error messages instead of infinite loops
  • TypeScript support - Full type definitions and TypeScript file execution via tsx/ts-node

Table of Contents


Getting Started

Installation

As a library dependency:

npm install configorama

As a global CLI tool:

npm install -g configorama

Quick Start

Async API (recommended for most use cases):

const path = require('path')
const configorama = require('configorama')
const cliFlags = require('minimist')(process.argv.slice(2))

// Path to yaml/json/toml config
const myConfigFilePath = path.join(__dirname, 'config.yml')

// Execute config resolution asynchronously
const config = await configorama(myConfigFilePath, { options: cliFlags })

console.log(config) // resolved config

Sync API (for synchronous execution contexts):

const path = require('path')
const configorama = require('configorama')
const cliFlags = require('minimist')(process.argv.slice(2))

// Path to yaml/json/toml config
const myConfigFilePath = path.join(__dirname, 'config.yml')

// Execute config resolution synchronously
const config = configorama.sync(myConfigFilePath, { options: cliFlags })

console.log(config) // resolved config

Example configuration file (config.yml):

# Environment variable
apiKey: ${env:API_KEY}

# CLI option (e.g., --stage prod)
environment: ${opt:stage, 'dev'}

# Self-reference to other values
service: my-app
fullName: ${service}-api

# File reference
secrets: ${file(./secrets.yml)}

# Git information
branch: ${git:branch}
commit: ${git:sha1}

# Conditional logic
memorySize: ${if(${environment} === 'prod' ? 1024 : 512)}

# Nested references
database:
  host: ${env:DB_HOST, 'localhost'}
  port: ${env:DB_PORT, 5432}
  name: ${service}-${environment}

Running Examples

The project includes example files demonstrating various features:

# Clone the repository
git clone https://github.com/DavidWells/configorama
cd configorama

# Install dependencies
npm install

# Run async API example
node examples/using-async-api.js --stage prod

# Run sync API example
node examples/using-sync-api.js --stage dev

# Run zero-config example
node examples/zero-config.js

# Run TypeScript example
node examples/typescript/using-typescript.js

How It Works

Resolution Flow

Configorama creates a dependency graph of your config file and all its dependencies, then resolves values based on their variable sources. The resolution process follows this flow:

flowchart TD
    A[Load config file] --> B[Parse yml/json/toml/hcl to object]
    B --> C[Preprocess: raw config file]
    C --> D{Return metadata only?}
    D -->|Yes| E[Collect variable metadata]
    E --> F[Return found variable metadata + original config]
    D -->|No| G[Traverse & resolve variables recursively]
    G --> H[Post-process: runs filters and functions]
    H --> I[Return resolved config]

Resolution process:

  1. Load - Read config file from disk or accept JavaScript object
  2. Parse - Convert to JavaScript object (format auto-detected by extension)
  3. Preprocess - Identify all variables and build dependency graph
  4. Traverse - Recursively resolve variables in dependency order
  5. Post-process - Apply filters and functions
  6. Return - Fully resolved configuration object

Analyzing Without Resolving

Analyze config structure and variables without actually resolving them:

const result = await configorama.analyze('config.yml')

// Returns metadata about variables without resolving them
console.log(result.originalConfig)   // Raw config object
console.log(result.variables)        // All variables found
console.log(result.uniqueVariables)  // Variables grouped by name
console.log(result.fileDependencies) // File references found

Use cases:

  • Validate config structure before deployment
  • Generate documentation of required environment variables
  • Build dependency graphs for complex configs
  • Audit what external resources a config depends on

Getting Metadata

Resolve config and get detailed metadata about the resolution process:

const result = await configorama('config.yml', {
  returnMetadata: true,
  options: { stage: 'prod' }
})

// Returns both resolved config and metadata
console.log(result.config)                    // Fully resolved config
console.log(result.originalConfig)            // Raw config object
console.log(result.metadata.variables)        // Variable info with resolution details
console.log(result.metadata.fileDependencies) // All file dependencies
console.log(result.metadata.summary)          // { totalVariables, requiredVariables, variablesWithDefaults }
console.log(result.resolutionHistory)         // Step-by-step resolution for each path

Metadata structure:

{
  config: { /* resolved config */ },
  originalConfig: { /* raw config */ },
  metadata: {
    variables: [
      {
        variable: '${env:API_KEY}',
        variableType: 'env',
        variableName: 'API_KEY',
        variablePath: 'apiKey',
        defaultValue: null,
        hasDefault: false,
        resolved: true,
        resolvedValue: 'secret-key-123'
      },
      // ... more variables
    ],
    summary: {
      totalVariables: 15,
      requiredVariables: 8,
      variablesWithDefaults: 7
    },
    fileDependencies: ['./secrets.yml', './config.ts']
  },
  resolutionHistory: {
    'apiKey': [
      { step: 1, value: '${env:API_KEY}', type: 'env' },
      { step: 2, value: 'secret-key-123', resolved: true }
    ]
  }
}

Architecture

┌──────────────────┐    ┌─────────────────────┐    ┌──────────────────┐
│  Input           │───▶│  Configorama core   │───▶│  Output          │
│                  │    │                     │    │                  │
│  • Config file   │    │  parser registry    │    │  Resolved config │
│  • JS/TS object  │    │  (yaml, json, toml, │    │  (+ metadata if  │
│  • Inline opts   │    │   ini, hcl, md, …)  │    │   requested)     │
└──────────────────┘    │                     │    └──────────────────┘
                        │  preProcess()       │
                        │  ↓                  │
                        │  populateObject()   │◀───┐  iterates until
                        │  ↓                  │    │  no variables
                        │  resolve leaf vars  │────┘  remain
                        │  ↓                  │
                        │  apply filters/funcs│
                        │  ↓                  │
                        │  return             │
                        └──────────┬──────────┘
                                   │
                                   ▼
                ┌──────────────────────────────────────┐
                │           Variable Sources           │
                │ ┌────────┐ ┌────────┐ ┌────────────┐ │
                │ │ env    │ │ opt    │ │ file/text  │ │
                │ │ self   │ │ param  │ │ git/cron   │ │
                │ │ eval   │ │ if     │ │ + plugins  │ │
                │ └────────┘ └────────┘ └────────────┘ │
                │                                      │
                │  Bundled plugins:                    │
                │  • plugins/cloudformation            │
                │                                      │
                │  Custom: variableSources: [{…}]      │
                └──────────────────────────────────────┘

Resolution is a fixed-point loop: each pass resolves what it can, then populateObject() runs again until no ${…} references remain. Built-in resolvers run first; custom resolvers from variableSources are tried in order.

Performance

A typical 21KB serverless-style config resolves in ~3ms on warm Node 22.

  • Before/after benchmarks against the published 0.9.17 baseline: PERF.md
  • Reproducible bench harness: scripts/bench.js
  • Run against your own configs:
    node scripts/bench.js  # local
    node scripts/bench.js /path/to/another/configorama  # A/B

If your config is slow, please open an issue with the config (or a redacted reproduction). We're happy to profile and tighten the hot path.


Variable Sources

Configorama supports multiple variable sources. All variable syntax follows the pattern ${type:value} or ${type(value)}.

Summary Table

| Variable | Syntax | Description | Example | |----------|-----------------------|------------------------|---------| | env | ${env:VAR} | Environment variables | ${env:NODE_ENV} | | option | ${option:flag} or ${opt:flag} | CLI option flags (opt is shorthand) | ${option:stage} | | param | ${param:key} | Parameter values | ${param:domain} | | self | ${key} or ${self:key} | Self references | ${database.host} | | file | ${file(path)} | File references | ${file(./secrets.yml)} | | text | ${text(path)} | Raw text file | ${text(./README.md)} | | git | ${git:value} | Git data | ${git:branch} | | cron | ${cron(expr)} | Cron expressions | ${cron('every 5 minutes')} | | eval | ${eval(expr)} | Math/logic expressions | ${eval(10 + 5)} | | if | ${if(expr)} | Conditional expressions| ${if(x > 5 ? 'yes' : 'no')} |


Environment Variables

Access values from process.env environment variables.

# Basic env var
apiKey: ${env:SECRET_KEY}

# With fallback default if env var not found
apiKeyWithFallback: ${env:SECRET_KEY, 'defaultApiKey'}

# Common patterns
nodeEnv: ${env:NODE_ENV, 'development'}
port: ${env:PORT, 3000}
debug: ${env:DEBUG, false}

How it works:

  • Reads from process.env at resolution time
  • Supports default values with comma syntax
  • Throws error if env var not found and no default provided (unless allowUnresolvedVariables is set)

CLI usage:

# Set env var then run
SECRET_KEY=abc123 node app.js

# Or export first
export SECRET_KEY=abc123
node app.js

CLI Option Flags

Access values from command line arguments passed via the options parameter.

# CLI option. Example `cmd --stage dev` makes `bar: dev`
bar: ${opt:stage}

# Composed example makes `foo: dev-hello`
foo: ${opt:stage}-hello

# With default value. If no --stage flag, uses 'dev'
environment: ${opt:stage, 'dev'}

# Boolean flags
verbose: ${opt:verbose, false}

# Nested paths
region: ${opt:aws.region, 'us-east-1'}

How it works:

  • Reads from the options object passed to configorama
  • Typically populated from CLI args using minimist or similar parser
  • Supports dot-notation for nested option paths

Example:

const minimist = require('minimist')
const configorama = require('configorama')

const argv = minimist(process.argv.slice(2))
// argv = { stage: 'prod', verbose: true, aws: { region: 'eu-west-1' } }

const config = await configorama('config.yml', { options: argv })
# Command line
node app.js --stage prod --verbose --aws.region eu-west-1

Parameter Values

Access parameter values via ${param:key}. Parameters follow a resolution hierarchy:

  1. CLI params (--param="key=value") - highest priority
  2. Stage-specific params (stages.<stage>.params)
  3. Default params (stages.default.params)
# Direct parameter reference
appDomain: ${param:domain}

# Parameter with fallback
apiKey: ${param:apiKey, 'default-api-key'}

# Stage-specific parameters defined in config
stages:
  dev:
    params:
      domain: dev.myapp.com
      dbHost: localhost
  prod:
    params:
      domain: myapp.com
      dbHost: prod-db.myapp.com
  default:
    params:
      domain: default.myapp.com
      dbPort: 3306

CLI Usage:

# Single param
node app.js --param="domain=example.com"

# Multiple params
node app.js --param="domain=example.com" --param="apiKey=secret123"

# With stage selection
node app.js --stage prod --param="domain=cli-override.com"

Code Usage:

const config = await configorama('config.yml', {
  options: {
    stage: 'prod',
    param: ['domain=cli-override.com', 'apiKey=secret']
  }
})

Resolution order example:

stages:
  prod:
    params:
      domain: prod.myapp.com  # 2. Stage-specific
  default:
    params:
      domain: default.myapp.com  # 3. Default fallback

appUrl: ${param:domain}
# CLI override (highest priority)
node app.js --stage prod --param="domain=cli.myapp.com"
# Result: appUrl = 'cli.myapp.com'

# Stage param (no CLI override)
node app.js --stage prod
# Result: appUrl = 'prod.myapp.com'

# Default param (no CLI override, no stage match)
node app.js --stage staging
# Result: appUrl = 'default.myapp.com'

Self References

Reference values from other key paths in the same configuration file using dot notation.

foo: bar

zaz:
  matazaz: 1
  wow:
    cool: 2

# Shorthand dot.prop reference
two: ${foo}  # Resolves to 'bar'

# Explicit self file reference
one: ${self:foo}  # Resolves to 'bar'

# Dot prop reference traverses objects
three: ${zaz.wow.cool}  # Resolves to 2

# Complex nested references
database:
  host: localhost
  port: 5432
  name: mydb

connectionString: postgres://${database.host}:${database.port}/${database.name}
# Resolves to: postgres://localhost:5432/mydb

# Array access
items:
  - first
  - second
  - third

selectedItem: ${items[1]}  # Resolves to 'second'

How it works:

  • Uses dot-notation for nested object access
  • Supports array index access with bracket notation
  • Resolves in dependency order (referenced values resolved first)
  • Detects circular references and throws helpful errors

File References

Import values from external yml, json, toml, hcl, or other supported files by relative path.

# Import full yml/json/toml/hcl file via relative path
fileRef: ${file(./subFile.yml)}

# Import sub values from files (topLevel key from other-config.yml)
fileValue: ${file(./other-config.yml):topLevel}

# Import nested sub values (nested.value from other-config.json)
fileValueSubKey: ${file(./other-config.json):nested.value}

# Fallback to default value if file not found
fallbackValueExample: ${file(./not-found.yml), 'fall back value'}

# Relative paths from config file location
secrets: ${file(../shared/secrets.yml)}

# Import from subdirectory
dbConfig: ${file(./config/database.yml):production}

Supported file types (extensions are case-insensitive):

| Type | Extensions | |------|------------| | TypeScript | .ts, .tsx, .mts, .cts | | JavaScript | .js, .cjs | | ESM | .mjs, .esm | | YAML | .yml, .yaml | | TOML | .toml, .tml | | INI | .ini | | JSON | .json, .json5, .jsonc | | HCL (Terraform) | .tf, .hcl, .tf.json | | Markdown | .md, .mdx, .markdown, .mdown, .mkdn, .mkd |

Path resolution:

  • Relative paths resolved from config file's directory
  • Absolute paths supported
  • ~ home directory expansion NOT supported (use absolute paths)

Example file structure:

project/
├── config.yml            # Main config
├── secrets.yml           # Secrets file
└── environments/
    ├── dev.yml
    └── prod.yml
# config.yml
secrets: ${file(./secrets.yml)}
environment: ${file(./environments/${opt:stage}.yml)}

Sync/Async File References

Execute JavaScript files and use their exported function's return value. Functions can be synchronous or asynchronous and receive arguments from your config.

# Async function execution
asyncJSValue: ${file(./async-value.js)}

# Sync function execution
syncJSValue: ${file(./sync-value.js)}

# With arguments (resolved before being passed)
secrets: ${file(./fetch-secrets.js, ${self:environment}, ${self:region})}

JavaScript file example (async-value.js):

async function fetchSecretsFromRemoteStore() {
  // Simulate async operation (AWS Secrets Manager, HashiCorp Vault, etc.)
  await new Promise(resolve => setTimeout(resolve, 1000))
  return {
    apiKey: 'secret-key-123',
    dbPassword: 'db-password-456'
  }
}

module.exports = fetchSecretsFromRemoteStore

Sync function example (sync-value.js):

function getEnvironmentConfig() {
  return {
    timeout: 5000,
    retries: 3,
    logLevel: process.env.NODE_ENV === 'production' ? 'error' : 'debug'
  }
}

module.exports = getEnvironmentConfig

Passing Arguments to Functions

You can pass resolved values from your config as arguments to JavaScript/TypeScript functions:

foo: bar
baz:
  qux: quux

# Pass resolved values as arguments
secrets: ${file(./fetch-secrets.js, ${self:foo}, ${self:baz})}

Arguments are passed in order, with the config context always last:

/**
 * @param {string} foo - First arg from YAML ('bar')
 * @param {object} baz - Second arg from YAML ({ qux: 'quux' })
 * @param {import('configorama').ConfigContext} ctx - Config context (always last)
 */
async function fetchSecrets(foo, baz, ctx) {
  console.log(foo)  // 'bar'
  console.log(baz)  // { qux: 'quux' }

  // Access config context
  console.log(ctx.originalConfig)  // Original unresolved config
  console.log(ctx.currentConfig)   // Current partially-resolved config
  console.log(ctx.options)         // Options passed to configorama

  return { secret: 'value' }
}

module.exports = fetchSecrets

ConfigContext

The ctx parameter (always the last argument) provides access to:

| Property | Description | |----------|-------------| | originalConfig | The original unresolved configuration object | | currentConfig | The current (partially resolved) configuration | | options | Options passed to configorama (populates ${option:xyz} / ${opt:xyz} variables) |

TypeScript users can import the type:

import type { ConfigContext } from 'configorama'

async function fetchSecrets(
  foo: string,
  baz: { qux: string },
  ctx: ConfigContext
): Promise<string> {
  // Full type support for ctx properties
  return 'secret-value'
}

export = fetchSecrets

Functions Without Arguments

If you don't need arguments, the function still receives ctx as its only parameter:

// No args - ctx is the only parameter
async function getSecrets(ctx) {
  return ctx.options.stage === 'prod'
    ? 'prod-secret'
    : 'dev-secret'
}

module.exports = getSecrets

TypeScript File References

Execute TypeScript files using tsx (recommended) or ts-node.

Installation:

# Recommended: Modern, fast TypeScript execution
npm install tsx --save-dev

# Alternative: Traditional ts-node approach
npm install ts-node typescript --save-dev

Usage in config:

# TypeScript configuration object
config: ${file(./config.ts)}

# TypeScript async function
secrets: ${file(./async-secrets.ts)}

# Specific property from TypeScript export
database: ${file(./config.ts):database}

# With arguments
apiConfig: ${file(./config.ts, ${opt:stage})}

TypeScript Object Export (typescript-config.ts):

interface DatabaseConfig {
  host: string
  port: number
  database: string
  ssl: boolean
}

interface ApiConfig {
  baseUrl: string
  timeout: number
  retries: number
}

interface ConfigObject {
  environment: string
  database: DatabaseConfig
  api: ApiConfig
  features: {
    enableNewFeature: boolean
    debugMode: boolean
  }
}

function createConfig(): ConfigObject {
  return {
    environment: process.env.STAGE || 'development',
    database: {
      host: process.env.DB_HOST || 'localhost',
      port: parseInt(process.env.DB_PORT || '5432'),
      database: process.env.DB_NAME || 'myapp',
      ssl: process.env.NODE_ENV === 'production'
    },
    api: {
      baseUrl: process.env.API_BASE_URL || 'http://localhost:3000',
      timeout: 5000,
      retries: 3
    },
    features: {
      enableNewFeature: process.env.STAGE === 'production',
      debugMode: process.env.DEBUG === 'true'
    }
  }
}

export = createConfig

TypeScript Async Function (typescript-async.ts):

interface SecretStore {
  apiKey: string
  dbPassword: string
  jwtSecret: string
}

function delay(ms: number): Promise<void> {
  return new Promise(resolve => setTimeout(resolve, ms))
}

async function fetchSecretsFromVault(): Promise<SecretStore> {
  console.log('Fetching secrets from vault...')

  // Simulate async operations (AWS Secrets Manager, HashiCorp Vault, etc.)
  await delay(100)

  return {
    apiKey: process.env.API_KEY || 'dev-api-key',
    dbPassword: process.env.DB_PASSWORD || 'dev-password',
    jwtSecret: process.env.JWT_SECRET || 'dev-jwt-secret'
  }
}

export = fetchSecretsFromVault

Complete Example Configuration:

# config-with-typescript.yml
service: my-awesome-app

# Load configuration from TypeScript file
provider: ${file(./typescript-config.ts)}

# Load secrets asynchronously from TypeScript file
secrets: ${file(./typescript-async.ts)}

# Mix TypeScript with other configuration
custom:
  stage: ${opt:stage, "dev"}
  region: ${opt:region, "us-east-1"}

  # Use TypeScript files for specific sections
  databaseConfig: ${file(./typescript-config.ts):database}

  # Environment-specific overrides
  stageVariables:
    dev:
      logLevel: debug
    prod:
      logLevel: info

# Regular configuration values
resources:
  description: "Configuration loaded with TypeScript support"

functions:
  hello:
    handler: handler.hello
    environment:
      LOG_LEVEL: ${self:custom.stageVariables.${self:custom.stage}.logLevel}
      DB_HOST: ${self:provider.database.host}
      API_KEY: ${self:secrets.apiKey}

Features:

  • Modern tsx execution (fast, no compilation) with ts-node fallback
  • Support for both sync and async TypeScript functions
  • Function argument passing via config variables
  • Full TypeScript interface support
  • Errors point to the failing dependency

Terraform HCL Support

Configorama supports Terraform HCL (HashiCorp Configuration Language) files, allowing you to parse .tf, .tf.json, and .hcl files.

Installation:

HCL parsing requires the optional @cdktf/hcl2json package:

npm install @cdktf/hcl2json

Supported file types:

  • .tf - Terraform configuration files
  • .hcl - Generic HCL files
  • .tf.json - Terraform JSON configuration files

Example:

const configorama = require('configorama')

// Parse a Terraform configuration file
const terraformConfig = await configorama('./main.tf')

// Access Terraform variables, resources, locals, etc.
console.log(terraformConfig.variable)  // Variables defined in the file
console.log(terraformConfig.resource)  // Resources
console.log(terraformConfig.locals)    // Local values
console.log(terraformConfig.output)    // Outputs

Importing Terraform files:

# Import Terraform variables from a .tf file
terraformVars: ${file(./terraform/variables.tf)}

# Import specific variable from Terraform file
region: ${file(./terraform/variables.tf):variable.region[0].default}

Variable syntax:

When loading .tf or .hcl files directly, configorama automatically uses $[...] syntax instead of ${...} to avoid conflicts with Terraform's native ${var.name} interpolation. Terraform expressions like ${var.environment} and ${map(string)} are preserved as-is.

// Loading .tf directly - uses $[...] syntax automatically
const config = await configorama('./main.tf')
// config.locals[0].app_name = "myapp-${var.environment}" (preserved)

// Use $[...] for configorama variables in .tf files
// myvar: $[env:MY_VAR]
// myref: $[file(./other.yml)]  # referenced files also use $[...]

When importing .tf files from other config formats (yml, json, etc.) via ${file()}, the parent file's syntax applies. Use allowUnknownVariableTypes: true if the imported .tf contains Terraform interpolations:

const config = await configorama('./config.yml', {
  allowUnknownVariableTypes: true
})

Read-only support:

Currently, HCL files can be read and parsed, but writing/generating HCL files is not supported.

See tests/hclTests for example Terraform files.


Git References

Access repository information from the current working directory's git data.

########################
# Git Variables
########################

# Repo owner/name. E.g. DavidWells/configorama
repo: ${git:repo}
repository: ${git:repository}

# Repo owner. E.g. DavidWells
owner: ${git:owner}
repoOwner: ${git:repoOwner}
repoOwnerDashed: ${git:repo-owner}

# Url. E.g. https://github.com/DavidWells/configorama
url: ${git:url}
repoUrl: ${git:repoUrl}
repoUrlDashed: ${git:repo-url}

# Directory. E.g. https://github.com/DavidWells/configorama/tree/master/tests/gitVariables
dir: ${git:dir}
directory: ${git:directory}

# Branch
branch: ${git:branch}

# Commits. E.g. 785fa6b982d67b079d53099d57c27fa87c075211
commit: ${git:commit}

# Sha1. E.g. 785fa6b
sha1: ${git:sha1}

# Message. E.g. 'Initial commit'
message: ${git:message}

# Remotes. E.g. https://github.com/DavidWells/configorama
remote: ${git:remote}
remoteDefined: ${git:remote('origin')}
remoteDefinedNoQuotes: ${git:remote(origin)}

# Tags. E.g. v0.5.2-1-g785fa6b
tag: ${git:tag}
# Describe. E.g. v0.5.2-1-g785fa6b
describe: ${git:describe}

# Timestamp. E.g. 2025-01-28T07:28:53.000Z
gitTimestampRelativePath: ${git:timestamp('../../package.json')}
# Timestamp. E.g. 2025-01-28T07:28:53.000Z
gitTimestampAbsolutePath: ${git:timestamp('package.json')}

How it works:

  • Reads git data from .git directory in current working directory or parent directories
  • Executes git commands via child process
  • Throws error if not in a git repository

Cron Values

Convert human-readable time expressions into standard cron syntax.

# Basic patterns
everyMinute: ${cron('every minute')}        # * * * * *
everyHour: ${cron('every hour')}            # 0 * * * *
everyDay: ${cron('every day')}              # 0 0 * * *
weekdays: ${cron('weekdays')}               # 0 0 * * 1-5
midnight: ${cron('midnight')}               # 0 0 * * *
noon: ${cron('noon')}                       # 0 12 * * *

# Interval patterns
every5Minutes: ${cron('every 5 minutes')}   # */5 * * * *
every15Minutes: ${cron('every 15 minutes')} # */15 * * * *
every2Hours: ${cron('every 2 hours')}       # 0 */2 * * *
every3Days: ${cron('every 3 days')}         # 0 0 */3 * *

# Specific times
at930: ${cron('at 9:30')}                   # 30 9 * * *
at930pm: ${cron('at 9:30 pm')}              # 30 21 * * *
at1200: ${cron('at 12:00')}                 # 0 12 * * *
at1230am: ${cron('at 12:30 am')}            # 30 0 * * *

# Weekday patterns
mondayMorning: ${cron('on monday at 9:00')}  # 0 9 * * 1
fridayEvening: ${cron('on friday at 17:00')} # 0 17 * * 5
sundayNoon: ${cron('on sunday at 12:00')}    # 0 12 * * 0

# Pre-existing cron expressions (pass through)
customCron: ${cron('15 2 * * *')}           # 15 2 * * *

Supported expressions:

  • every N minutes/hours/days
  • at HH:MM [am/pm]
  • on [weekday] at HH:MM
  • midnight, noon, weekdays
  • Standard cron syntax (passed through unchanged)

Eval Expressions

Evaluate mathematical and logical expressions safely (without using JavaScript's eval). Uses the subscript library for safe expression evaluation.

# Math operations
sum: ${eval(10 + 5)}                  # 15
multiply: ${eval(10 * 3)}             # 30
divide: ${eval(100 / 4)}              # 25
modulo: ${eval(17 % 5)}               # 2

# Comparisons (returns boolean)
isGreater: ${eval(200 > 100)}         # true
isLess: ${eval(100 > 200)}            # false
isEqual: ${eval(10 == 10)}            # true

# String comparisons
isEqual: ${eval("hello" == "hello")}  # true
strictEqual: ${eval("foo" === "foo")} # true
notEqual: ${eval("a" != "b")}         # true

# Complex expressions
complex: ${eval((10 + 5) * 2)}        # 30
percentage: ${eval((75 / 100) * 200)} # 150

# With variables
threshold: 50
value: 75
aboveThreshold: ${eval(${value} > ${threshold})}  # true

Supported operators:

| Category | Operators | |----------|-----------| | Arithmetic | + - * / % | | Comparison | == != === !== > < >= <= | | Logical | && \|\| ! | | Grouping | ( ) |

Security:

  • Does NOT use JavaScript's eval()
  • Uses safe expression parser (subscript)
  • No access to global scope or functions
  • Only mathematical and logical operations allowed

If Expressions

Conditional expressions using ternary syntax. This is an alias for eval with a clearer name for conditionals.

# Basic ternary (condition ? "yes" : "no")
status: ${if(5 > 3 ? "yes" : "no")}           # "yes"

# With variables
threshold: 50
value: 75
result: ${if(${value} > ${threshold} ? "above" : "below")}  # "above"

# Nested ternary (if/else if/else)
score: 85
grade: ${if(${score} >= 90 ? "A" : ${score} >= 80 ? "B" : "C")}  # "B"

# Boolean result (no ternary needed)
isValid: ${if(${value} > 0)}               # true

# Logical operators
enabled: true
count: 5
canProceed: ${if(${enabled} && ${count} > 0)}  # true
hasIssues: ${if(!${enabled} || ${count} == 0)} # false

Supported operators:

| Category | Operators | |----------|-----------| | Comparison | == != === !== > < >= <= | | Logical | && \|\| ! | | Nullish | ?? | | Ternary | condition ? "yes" : "no" |

Serverless deployment examples:

service: my-service

provider:
  name: aws
  stage: ${opt:stage, 'dev'}
  region: ${opt:region, 'us-east-1'}

custom:
  # Different memory by stage
  memorySize: ${if(${provider.stage} === "prod" ? 1024 : 512)}

  # Different log retention by stage
  logRetention: ${if(${provider.stage} === "prod" ? 30 : 7)}

  # Enable features per environment
  enableDebugEndpoints: ${if(${provider.stage} !== "prod")}
  enableMetrics: ${if(${provider.stage} === "prod")}

  # Regional settings
  replicaCount: ${if(${provider.region} === "us-east-1" ? 3 : 1)}

  # Conditional IAM role (use predefined role in prod, inline in dev)
  useExternalRole: ${if(${provider.stage} === "prod")}
  role: ${if(${custom.useExternalRole} ? "arn:aws:iam::123:role/prod-role" : null)}

functions:
  api:
    handler: handler.api
    memorySize: ${custom.memorySize}

  # Debug function - only deployed in non-prod
  debug:
    handler: handler.debug
    enabled: ${custom.enableDebugEndpoints}

  # Metrics processor - only in prod
  metricsProcessor:
    handler: handler.metrics
    enabled: ${custom.enableMetrics}

Filters (Experimental)

Pipe resolved values through transformation functions like case conversion.

# String transformations
toUpperCaseString: ${'value' | toUpperCase }  # 'VALUE'
toLowerCaseString: ${'VALUE' | toLowerCase }  # 'value'

# Case conversions
toKebabCaseString: ${'valueHere' | toKebabCase }  # 'value-here'
toCamelCaseString: ${'value-here' | toCamelCase } # 'valueHere'

# Chaining filters
key: lol_hi
transformed: ${key | toKebabCase | toUpperCase }  # 'LOL-HI'

# With variables
serviceName: MyServiceName
serviceSlug: ${serviceName | toKebabCase}  # 'my-service-name'

Built-in filters:

  • toUpperCase - Convert to uppercase
  • toLowerCase - Convert to lowercase
  • toKebabCase - Convert to kebab-case
  • toCamelCase - Convert to camelCase
  • String, Number, Boolean, Array, Object, Json - Validate/coerce resolved values
  • oneOf(...) - Restrict a value to inline literals or a resolved list variable
  • help('text') - Attach guidance to a variable for the config wizard; returns the value unchanged

The help() filter is an identity filter: it leaves the value untouched but records prompt/agent description text.

apiKey: ${env:API_KEY | help('The Stripe live secret key')}
stage: ${option:stage | toUpperCase | help('Deployment stage')}
dbPort: ${env:DB_PORT, 5432 | Number | help('The Postgres port')}

oneOf() is a runtime constraint. It throws if the resolved value is not in the allowed set. Put type filters first when coercion matters:

stage: ${option:stage | oneOf('dev', 'staging', 'prod')}
threads: ${option:threads | Number | oneOf(1, 2, 4)}

allowedStages:
  - dev
  - prod
stageFromList: ${option:stage | oneOf(${self:allowedStages})}

Array accepts existing arrays, JSON/JSON5 array strings, and comma-separated text. Object accepts existing objects and JSON/JSON5 object strings.

Comments are used as help fallback when help() is absent. Precedence is help() first, then trailing inline comments, then a leading comment block:

# Used by deploy jobs
deployToken: ${env:DEPLOY_TOKEN}
region: ${option:region, 'us-east-1'} # AWS region

Use comment annotations for human and agent metadata. Filters affect runtime values; comments describe values:

secrets:
  # Stripe live secret key
  # @from Stripe dashboard > Developers > API keys
  # @example sk_live_...
  # @default Set in CI or local shell profile
  # @sensitive true
  # @group Payments
  # @deprecated Use STRIPE_RESTRICTED_KEY instead
  stripeSecret: ${env:STRIPE_SECRET_KEY}

Supported annotation tags:

  • @description ... - Explicit description; overrides normal comment text and help()
  • @from ... - Where to obtain the value; appears as obtainHint
  • @example ... - Example value; can appear multiple times
  • @default ... - Documentation-only default hint; does not resolve the variable
  • @sensitive true|false - Override name-based masking detection
  • @group ... - Wizard display group label
  • @deprecated ... - Warning text for requirements JSON and prompt descriptors

from() and meta() are not built-in filters. JSON files cannot use comment annotations because JSON has no comments; use JSON5/JSONC or another commented format if metadata is needed.

Custom filters:

const config = await configorama('config.yml', {
  filters: {
    // Custom filter
    reverse: (value) => value.split('').reverse().join(''),
    // Filter with options
    truncate: (value, length = 10) => value.substring(0, length)
  }
})
# Using custom filters
reversed: ${'hello' | reverse}  # 'olleh'
truncated: ${'very long string' | truncate(5)}  # 'very '

Functions (Experimental)

Apply built-in functions to combine, transform, or manipulate values.

object:
  one: once
  two: twice

objectTwo:
  three: third
  four: fourth

# Merge objects
mergeObjects: ${merge(${object}, ${objectTwo})}
# Result: { one: 'once', two: 'twice', three: 'third', four: 'fourth' }

# String concatenation
fullName: ${concat(${firstName}, ' ', ${lastName})}

# Array operations
items:
  - a
  - b
  - c

joinedItems: ${join(${items}, ', ')}  # 'a, b, c'

Built-in functions:

  • merge(obj1, obj2, ...) - Merge multiple objects
  • concat(str1, str2, ...) - Concatenate strings
  • join(array, separator) - Join array elements

Custom functions:

const config = await configorama('config.yml', {
  functions: {
    // Custom function
    add: (a, b) => a + b,
    // Function with multiple args
    between: (val, min, max) => val >= min && val <= max
  }
})
# Using custom functions
sum: ${add(5, 10)}  # 15
value: 75
inRange: ${between(${value}, 50, 100)}  # true

Bundled Plugins

Plugins ship in the repo under plugins/ and are opt-in: install their peer dependencies, then wire them into variableSources. Plugins are not required dependencies of configorama itself, so consumers who don't need them aren't paying for them.

CloudFormation

Resolves CloudFormation stack output values. Single-region, multi-region, and multi-account.

# Default region, default AWS credentials
apiUrl: ${cf:my-stack.ApiUrl}

# Explicit region
westUrl: ${cf(us-west-2):west-stack.ApiUrl}

# Cross-account: 'prod' matches PROD_AWS_ACCESS_KEY_ID env vars
prodUrl: ${cf(prod:us-west-2):prod-stack.ApiUrl}
const configorama = require('configorama')
const createCloudFormationResolver = require('configorama/plugins/cloudformation')

const cfResolver = createCloudFormationResolver({
  defaultRegion: 'us-east-1',
})

const config = await configorama('config.yml', {
  variableSources: [cfResolver]
})

Full docs: plugins/cloudformation/README.md. Covers the env-var-prefix alias convention, the refcounted credential mutex for parallel-safe deploys, and the skipResolution mode for CI metadata extraction.

Peer dependency (install separately):

npm install @aws-sdk/client-cloudformation @aws-sdk/credential-providers

1Password

Resolves secret values through the 1Password CLI (op). Secrets are fetched at resolution time — never persisted, never logged.

npmToken: ${op:npm.NPM_TOKEN}
dbPassword: ${op:database}
directRef: ${op(op://vault/item/field)}
const configorama = require('configorama')
const createOnePasswordResolver = require('configorama/plugins/onepassword')

const opResolver = createOnePasswordResolver({
  refs: {
    npm: 'op://production/npm-automation/notesPlain',
    database: { item: 'database-prod', vault: 'production', field: 'password' },
  },
})

const config = await configorama('config.yml', {
  variableSources: [opResolver]
})

Full docs: plugins/onepassword/README.md. Covers alias refs, private item links, field inference and ambiguity rules, INI/dotenv key paths, skipResolution, and sync usage.

No npm dependencies — requires the op binary on PATH and a signed-in CLI (or OP_SERVICE_ACCOUNT_TOKEN).


API Reference

Async API

The primary async API for resolving configurations.

Signature:

function configorama<T = any>(
  configPathOrObject: string | object,
  settings?: ConfigoramaSettings
): Promise<T | ConfigoramaResult<T>>

Parameters:

| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | configPathOrObject | string \| object | Yes | Path to config file or raw JavaScript object | | settings | ConfigoramaSettings | No | Configuration options |

Settings object:

interface ConfigoramaSettings {
  options?: Record<string, any>          // CLI flags for ${opt:xyz}
  syntax?: string | RegExp               // Custom variable syntax
  configDir?: string                     // Working directory for relative paths
  variableSources?: VariableSource[]     // Custom variable resolvers
  filters?: Record<string, Function>     // Custom filter functions
  functions?: Record<string, Function>   // Custom functions
  allowUnknownVariableTypes?: boolean | string[]  // Allow unknown var types
  allowUnresolvedVariables?: boolean | string[]   // Allow unresolved vars
  allowUndefinedValues?: boolean         // Allow undefined in output
  returnMetadata?: boolean               // Return metadata with config
  mergeKeys?: string[]                   // Keys to merge in arrays
  filePathOverrides?: Record<string, string>  // Override file paths
}

Returns:

  • If returnMetadata: false (default): Promise<T> - Resolved config object
  • If returnMetadata: true: Promise<ConfigoramaResult<T>> - Object with config and metadata

Example:

const configorama = require('configorama')

// Basic usage
const config = await configorama('./config.yml')

// With options
const config = await configorama('./config.yml', {
  options: { stage: 'prod', region: 'us-east-1' },
  allowUnknownVariableTypes: ['ssm', 'cf']
})

// With metadata
const result = await configorama('./config.yml', {
  returnMetadata: true,
  options: { stage: 'prod' }
})

console.log(result.config)           // Resolved config
console.log(result.metadata)         // Variable metadata
console.log(result.resolutionHistory) // Resolution steps

Sync API

Synchronous API for blocking config resolution.

Signature:

function configorama.sync<T = any>(
  configPathOrObject: string | object,
  settings?: ConfigoramaSettings
): T

Parameters:

Same as async API, but dynamicArgs cannot be a function (must be serializable).

Returns:

T - Resolved config object (synchronously)

Limitations:

  • Cannot use async functions in JavaScript/TypeScript file references
  • dynamicArgs must be serializable (not a function)
  • CLI args automatically parsed from process.argv if options not provided

Example:

const configorama = require('configorama')

// Basic sync usage
const config = configorama.sync('./config.yml')

// With options
const config = configorama.sync('./config.yml', {
  options: { stage: 'dev' }
})

Library API

const configorama = require('configorama')

const config = await configorama('config.yml')
const result = await configorama('config.yml', { returnMetadata: true })
const requirements = await configorama.inspect('config.yml', { view: 'requirements' })
const graph = await configorama.inspect('config.yml', { view: 'graph', format: 'mermaid' })
const configSync = configorama.sync('config.yml')

The stable public surface is intentionally small:

| Method | Use it for | |---|---| | configorama(fileOrObject, opts) | Async resolution. Set returnMetadata: true when you need metadata.variables, metadata.uniqueVariables, or metadata.fileDependencies. | | configorama.sync(fileOrObject, opts) | Synchronous resolution for blocking contexts. | | configorama.inspect(fileOrObject, opts) | Pre-resolution inspection. Use view: "requirements", view: "audit", or view: "graph" for one projection. | | configorama.format | Parser utilities for YAML, JSON/JSON5, TOML, INI, HCL, and Markdown frontmatter. |

Lower-level helpers still exist for compatibility: analyze(), introspect(), audit(), graph(), buildVariableSyntax(), and Configorama. New code should start with configorama(), configorama.sync(), or configorama.inspect().

returnMetadata: true remains the right API for tools that need resolved config plus dependency metadata:

const result = await configorama('serverless.yml', {
  returnMetadata: true,
  allowUnknownVariableTypes: true,
  allowUnresolvedVariables: true,
  options: { stage: 'prod' }
})

console.log(result.config)
console.log(result.metadata.variables)
console.log(result.metadata.uniqueVariables)
console.log(result.metadata.fileDependencies.resolvedPaths)
console.log(result.metadata.fileDependencies.globPatterns)

That metadata shape is the same path used by the newer inspection APIs, so tools can keep consuming it without switching to inspect().


Inspect API

Inspect config structure without resolving missing user inputs.

Signature:

function configorama.inspect(
  configPathOrObject: string | object,
  settings?: ConfigoramaSettings & {
    view?: 'requirements' | 'audit' | 'graph'
    format?: 'json' | 'mermaid' | 'dot'
  }
): Promise<object | string>

With no view, inspect() returns the full model:

const model = await configorama.inspect('./config.yml')

console.log(model.requirements)
console.log(model.graph)
console.log(model.audit)

Use view for one projection:

const configorama = require('configorama')

const requirements = await configorama.inspect('./config.yml', { view: 'requirements' })
const audit = await configorama.inspect('./config.yml', { view: 'audit' })
const graph = await configorama.inspect('./config.yml', { view: 'graph', format: 'mermaid' })

The older analyze(), introspect(), audit(), and graph() helpers map to the same underlying inspection paths. They are kept for existing consumers.


Format Utilities

Parse various config formats to JavaScript objects.

Available parsers:

const { format } = require('configorama')

// Parse YAML
const yamlObj = format.yaml.parse('key: value')

// Parse JSON (handles JSON5/JSONC too: comments, trailing commas)
const jsonObj = format.json.parse('{ key: "value", }')

// Parse TOML
const tomlObj = format.toml.parse('key = "value"')

// Parse INI
const iniObj = format.ini.parse('[section]\nkey=value')

// Parse HCL (requires @cdktf/hcl2json)
const hclObj = await format.hcl.parse('variable "example" { default = "value" }')

Available parsers: format.json, format.yaml, format.toml, format.ini, format.hcl, format.markdown.

Each has at minimum a parse(content) method; dump(obj) / stringify(obj) and cross-format converters (e.g. format.yaml.toJson, format.toml.toYaml) are available where the underlying format supports them. format.markdown is a frontmatter parser; see Markdown Files below.

Real use cases for format:

  • Parse a config file without resolving variables (just want the structure):
    const { format } = require('configorama')
    const fs = require('fs')
    const raw = format.yaml.parse(fs.readFileSync('config.yml', 'utf8'))
    // raw is the YAML structure with ${...} strings intact
  • Use the same YAML/TOML/JSON5 parsers as configorama itself in your own tooling, so a file that loads in one place loads identically in the other.

Markdown Files

Markdown configs (.md, .mdx, .markdown, .mdown, .mkdn, .mkd) are parsed as YAML/TOML/JSON frontmatter + body. The frontmatter becomes top-level keys; the body is exposed as _content on the resolved config (or _body if the frontmatter used that key explicitly).

---
service: my-service
stage: ${opt:stage, 'dev'}
---

# Service Docs
This is the body content.

Resolves to:

{
  service: 'my-service',
  stage: 'dev',
  _content: '# Service Docs\nThis is the body content.'
}

The body is detached during variable resolution (so ${…} inside the body text is left alone) and re-attached afterward; only frontmatter keys get variable expansion.


buildVariableSyntax(prefix, suffix, excludePatterns?)

Helper for building a properly-escaped regex source string to pass to the syntax option. Handles regex-special characters in your delimiters without you having to escape them yourself.

const { buildVariableSyntax } = require('configorama')

// Use {{ ... }} instead of ${ ... }
const syntax = buildVariableSyntax('{{', '}}')

const config = await configorama('config.yml', { syntax })

Parameters:

| Param | Type | Default | Description | |---|---|---|---| | prefix | string | '${' | Opening delimiter | | suffix | string | '}' | Closing delimiter | | excludePatterns | string[] | ['AWS', 'aws:', 'stageVariables'] | Patterns to exclude via negative lookahead (so e.g. ${AWS::Region} and ${aws:username} are left untouched by CloudFormation users) |


Configorama Class

For advanced use cases (long-lived instances, hooking into init/resolve lifecycle, accessing partial state) the underlying class is exported.

const { Configorama } = require('configorama')

const instance = new Configorama('config.yml', { options: { stage: 'dev' } })
await instance.init({ stage: 'dev' })
const resolved = await instance.populateObject(instance.config)
const metadata = instance.collectVariableMetadata()

Most users should prefer the top-level configorama() / .sync() / .analyze() functions, which are thin wrappers around this class.


configorama/parse-file Subpath

For tools that want to parse a config file (auto-detecting format from extension or contents) without going through variable resolution:

const { parseFile, parseFileContents } = require('configorama/parse-file')

// Read from disk
const raw = parseFile('./config.yml')
// returns the parsed object with ${...} strings intact

// Or parse already-loaded contents
const fromString = parseFileContents({
  contents: 'service: my-app\nstage: ${opt:stage}',
  filePath: 'in-memory.yml'
})

Both are synchronous. Useful for build tools that inspect or rewrite configs before handing them to configorama.


TypeScript Types

Type definitions are bundled (index.d.ts). TypeScript users get:

  • Generic typing on the resolved config: configorama<MyConfig>('config.yml') returns Promise<MyConfig>
  • Full typing on ConfigoramaSettings and ConfigoramaResult
  • Editor autocomplete on all options shown in the Complete Options Reference
import configorama, { ConfigoramaSettings } from 'configorama'

interface MyConfig {
  service: string
  stage: string
  database: { host: string; port: number }
}

const config = await configorama<MyConfig>('config.yml', { options: { stage: 'prod' } })
// config.database.port is typed as number

Configuration Options

Custom Variable Syntax

Use the syntax option to change the variable delimiters. You can provide a regex string directly or use buildVariableSyntax() to generate one with proper character escaping:

const configorama = require('configorama')
const { buildVariableSyntax } = require('configorama')

// Using buildVariableSyntax helper (recommended)
const config = await configorama(configFile, {
  syntax: buildVariableSyntax('{{', '}}'),  // Mustache-style: {{env:FOO}}
  options: { stage: 'dev' }
})

// Other examples:
buildVariableSyntax('${{', '}}')   // ${{env:FOO}}
buildVariableSyntax('#{', '}')     // #{env:FOO}
buildVariableSyntax('[[', ']]')    // [[env:FOO]]
buildVariableSyntax('<', '>')      // <env:FOO>

Function signature:

function buildVariableSyntax(
  prefix: string = '${',
  suffix: string = '}',
  excludePatterns: string[] = ['AWS', 'aws:', 'stageVariables']
): string

The buildVariableSyntax() function:

  • Automatically excludes suffix characters from the allowed character class (prevents parsing issues)
  • Supports nested variables by excluding $ and { from values
  • Third parameter excludePatterns is an array of strings to exclude via negative lookahead

Example with custom syntax:

const config = await configorama('config.yml', {
  syntax: buildVariableSyntax('{{', '}}')
})
# config.yml with {{ }} syntax
apiKey: {{env:API_KEY}}
stage: {{opt:stage, 'dev'}}
database: {{file(./db.yml)}}

allowUnknownVariableTypes

Controls what happens when encountering unregistered variable types (e.g., ${ssm:path} when ssm isn't a registered resolver).

Type: boolean | string[]

Default: false

Behavior:

// Allow ALL unknown types to pass through
const config = await configorama(configFile, {
  allowUnknownVariableTypes: true,
  options: { stage: 'dev' }
})
// Input:  { key: '${ssm:/path/to/secret}' }
// Output: { key: '${ssm:/path/to/secret}' }

// Allow only SPECIFIC unknown types
const config = await configorama(configFile, {
  allowUnknownVariableTypes: ['ssm', 'cf'],  // only these pass through
  options: { stage: 'dev' }
})
// ${ssm:path} and ${cf:stack.output} pass through
// ${custom:thing} throws an error

Use cases:

  • Multi-stage resolution (local resolution, then cloud provider resolves remaining vars)
  • Serverless Framework integration (let the framework resolve SSM and other refs it owns)
  • Gradual migration (allow unknown types during transition period)

CloudFormation refs (${cf:…}, ${cf(region):…}, ${cf(account:region):…}) are now resolved natively by the bundled plugins/cloudformation/ plugin; no external resolver required.


allowUnresolvedVariables

Controls what happens when a known resolver can't find a value (missing env vars, missing files, etc.).

Type: boolean | string[]

Default: false

Behavior:

// Allow ALL unresolved variables to pass through
const config = await configorama(configFile, {
  allowUnresolvedVariables: true,
  options: { stage: 'dev' }
})
// Input:  { key: '${env:MISSING_VAR}' }
// Output: { key: '${env:MISSING_VAR}' }

// Allow only SPECIFIC types to be unresolved
const config = await configorama(configFile, {
  allowUnresolvedVariables: ['param', 'file'],  // only these pass through
  options: { stage: 'prod' }
})
// Input:  { paramKey: '${param:x}', fileKey: '${file(missing.yml)}' }
// Output: { paramKey: '${param:x}', fileKey: '${file(missing.yml)}' }

// Mixed scenario
const config = await configorama(configFile, {
  allowUnresolvedVariables: ['param', 'file'],
  options: { stage: 'prod' }
})
// Input:  {
//   key: '${env:MISSING_VAR}',
//   paramKey: '${param:x}',
//   fileKey: '${file(missing.yml)}'
// }
// Output: Error thrown because ${env:MISSING_VAR} cannot resolve
// (param and file pass through, but env vars must resolve)

Important notes:

  • This option does NOT apply to self: or dotProp variables (e.g., ${foo.bar.baz})
  • Self-references are local config errors, not external dependencies
  • Useful for multi-stage resolution pipelines

Use cases:

  • Serverless Dashboard resolves params after local resolution
  • Gradual migration with optional external dependencies
  • Development mode where some services are unavailable

Complete Options Reference

| Option | Type | Default | Description | |--------|------|---------|-------------| | options | object | {} | CLI options/flags to populate ${option:xyz} / ${opt:xyz} variables | | syntax | string \| RegExp | ${...} | Custom variable syntax regex pattern | | configDir | string | directory of config file | Working directory for relative file paths | | variableSources | VariableSource[] | [] | Custom variable sources (see below) | | filters | Record<string, Function> | {} | Custom filter functions for pipe operator | | functions | Record<string, Function> | {} | Custom functions for ${fn(...)} syntax | | allowUnknownVariableTypes | boolean \| string[] | false | Allow unknown variable types to pass through | | allowUnresolvedVariables | boolean \| string[] | false | Allow known types that can't resolve to pass through | | allowUndefinedValues | boolean | false | Allow undefined as a valid end result | | ignorePaths | string[] | Built-in CloudFormation/code paths | Glob-like config paths whose values should be l