Skip to content

@memberjunction/config

Central configuration loading and merging utilities for the MemberJunction framework. This package provides a standardized way for MJ packages to define default configurations, discover user override files, and merge them together with a deterministic precedence order.

MemberJunction applications are configured through a layered system where each package defines its own defaults and users supply overrides through a shared mj.config.cjs file. This package provides the infrastructure that makes that layering work: config file discovery (via cosmiconfig), deep merge with customizable strategies, structure validation, and environment-variable parsing helpers.

flowchart TD
    A["Package Defaults<br/>(hardcoded in each package)"] --> D["mergeConfigs()"]
    B["mj.config.cjs<br/>(user overrides)"] --> D
    C["Environment Variables"] --> E["parseBooleanEnv()"]
    E --> F["Final Runtime Config"]
    D --> F

    style A fill:#2d6a9f,stroke:#1a4971,color:#fff
    style B fill:#2d8659,stroke:#1a5c3a,color:#fff
    style C fill:#b8762f,stroke:#8a5722,color:#fff
    style D fill:#7c5295,stroke:#563a6b,color:#fff
    style E fill:#7c5295,stroke:#563a6b,color:#fff
    style F fill:#2d6a9f,stroke:#1a4971,color:#fff
Terminal window
npm install @memberjunction/config

If you are working inside the MemberJunction monorepo, add the dependency to the consuming package’s package.json and run npm install at the repository root.

When loadMJConfig() is called it uses cosmiconfig to walk up the directory tree looking for the first matching file in the following order:

PriorityFile / LocationFormat
1mj.config.cjsCommonJS
2mj.config.jsESM / CommonJS
3.mjrcJSON / YAML
4.mjrc.jsESM / CommonJS
5.mjrc.cjsCommonJS
6"mj" key in package.jsonJSON

The recommended convention across MemberJunction is mj.config.cjs placed at the repository root.

The primary entry point. It discovers the user’s config file, merges it with the package defaults, and returns the result along with metadata about what was loaded.

import { loadMJConfig } from '@memberjunction/config';
interface MyPackageConfig {
port: number;
debug: boolean;
database: {
host: string;
pool: { max: number; min: number };
};
}
const MY_DEFAULTS: MyPackageConfig = {
port: 4000,
debug: false,
database: {
host: 'localhost',
pool: { max: 50, min: 5 }
}
};
const result = await loadMJConfig<MyPackageConfig>({
defaultConfig: MY_DEFAULTS,
verbose: true // logs discovery / merge details
});
console.log(result.config); // merged configuration object
console.log(result.hasUserConfig); // true if mj.config.cjs was found
console.log(result.configFilePath); // path to the discovered file
console.log(result.overriddenKeys); // top-level keys the user changed

For cases where an async call is not possible (CommonJS bootstrap code, for example), loadMJConfigSync accepts an explicit file path instead of searching the directory tree.

import { loadMJConfigSync } from '@memberjunction/config';
const config = loadMJConfigSync<MyPackageConfig>(
'/absolute/path/to/mj.config.cjs',
{ defaultConfig: MY_DEFAULTS }
);

buildMJConfig composes defaults from several MJ packages into a single configuration object before applying user overrides. This is typically called at application startup.

import { buildMJConfig } from '@memberjunction/config';
const config = buildMJConfig(
{
server: serverDefaults,
codegen: codegenDefaults,
mcpServer: mcpDefaults,
a2aServer: a2aDefaults,
queryGen: queryGenDefaults
},
userOverrides // optional -- from mj.config.cjs
);
flowchart LR
    S["Server<br/>Defaults"] --> B["buildMJConfig()"]
    C["CodeGen<br/>Defaults"] --> B
    M["MCP Server<br/>Defaults"] --> B
    A["A2A Server<br/>Defaults"] --> B
    Q["QueryGen<br/>Defaults"] --> B
    U["User<br/>Overrides"] --> B
    B --> R["Unified Config"]

    style S fill:#2d6a9f,stroke:#1a4971,color:#fff
    style C fill:#2d6a9f,stroke:#1a4971,color:#fff
    style M fill:#2d6a9f,stroke:#1a4971,color:#fff
    style A fill:#2d6a9f,stroke:#1a4971,color:#fff
    style Q fill:#2d6a9f,stroke:#1a4971,color:#fff
    style U fill:#2d8659,stroke:#1a5c3a,color:#fff
    style B fill:#7c5295,stroke:#563a6b,color:#fff
    style R fill:#b8762f,stroke:#8a5722,color:#fff

mergeConfigs performs a deep merge of two plain objects. It is used internally by the loader functions and is also exported for packages that need to merge configuration fragments on their own.

import { mergeConfigs } from '@memberjunction/config';
const merged = mergeConfigs(defaults, overrides, {
concatenateArrays: false, // true to append arrays instead of replacing
allowNullOverrides: false // true to let null values clear defaults
});
Source TypeBehavior
Primitive (string, number, boolean)Override replaces default
ObjectDeep recursive merge
ArrayOverride replaces default (or concatenates if concatenateArrays: true)
null / undefined in overrideIgnored by default; replaces if allowNullOverrides: true
Key with _append suffixConcatenates the array onto the matching base key

The _append suffix is especially useful when a user wants to add items to a default array without wiping it out:

mj.config.cjs
module.exports = {
// Instead of replacing excludeSchemas entirely, append to the defaults
excludeSchemas_append: ['staging', 'archive']
};

validateConfigStructure checks a merged configuration object against a set of expected top-level keys and logs warnings for any unexpected entries. This helps catch typos and deprecated settings early.

import { validateConfigStructure } from '@memberjunction/config';
const allowedKeys = new Set(['port', 'debug', 'database', 'logging']);
validateConfigStructure(config, allowedKeys);
// Warns: "Unexpected configuration keys found: databse"

parseBooleanEnv normalizes the many string representations of boolean values commonly found in environment variables into a strict true / false.

import { parseBooleanEnv } from '@memberjunction/config';
const debugMode = parseBooleanEnv(process.env.MJ_DEBUG);

Truthy values (case-insensitive): true, 1, yes, y, on, t. Everything else — including undefined, empty string, and null — returns false.

loadMJConfig<T>(options?): Promise<LoadConfigResult<T>>

Section titled “loadMJConfig<T>(options?): Promise<LoadConfigResult<T>>”

Asynchronously discovers and loads an MJ configuration file, merges it with the provided defaults, and returns the result.

loadMJConfigSync<T>(configPath, options?): T

Section titled “loadMJConfigSync<T>(configPath, options?): T”

Synchronously loads a configuration file from an explicit path and merges it with defaults. Does not search the directory tree.

buildMJConfig(packageDefaults, userConfigOverrides?): Record<string, unknown>

Section titled “buildMJConfig(packageDefaults, userConfigOverrides?): Record<string, unknown>”

Merges default configurations from multiple MJ packages into a single object, then applies optional user overrides.

mergeConfigs<T>(defaults, overrides, options?): T

Section titled “mergeConfigs<T>(defaults, overrides, options?): T”

Deep-merges two plain objects using customizable strategies for arrays, nulls, and the _append suffix convention.

validateConfigStructure(config, allowedKeys): void

Section titled “validateConfigStructure(config, allowedKeys): void”

Logs warnings for any top-level keys in config that are not present in allowedKeys.

Parses a string (typically from process.env) into a boolean using common truthy conventions.

Type guard that checks whether a value is a non-null object suitable for use as a configuration.

PropertyTypeDefaultDescription
searchFromstringprocess.cwd()Directory to start searching for the config file
requireConfigFilebooleanfalseThrow if no config file is found
mergeOptionsMergeOptions{}Controls array and null merge behavior
verbosebooleanfalseLog discovery and merge details to console
defaultConfigRecord<string, unknown>{}Base configuration provided by the calling package
PropertyTypeDescription
configTThe final merged configuration
configFilePathstring | undefinedPath to the discovered user config file
hasUserConfigbooleanWhether a user config file was found
overriddenKeysstring[]Top-level keys that differ from defaults
PropertyTypeDefaultDescription
concatenateArraysbooleanfalseAppend override arrays to defaults instead of replacing
allowNullOverridesbooleanfalseAllow null in overrides to clear default values
type MJConfig = Record<string, unknown>;

Generic configuration type. Each consuming package defines its own specific configuration interface.

flowchart TB
    subgraph ConfigPackage ["@memberjunction/config"]
        direction TB
        CL["config-loader.ts<br/>loadMJConfig / loadMJConfigSync / buildMJConfig"]
        CM["config-merger.ts<br/>mergeConfigs / validateConfigStructure"]
        CT["config-types.ts<br/>MJConfig / isValidConfig"]
        EU["env-utils.ts<br/>parseBooleanEnv"]
        CL --> CM
    end

    subgraph Consumers ["Consuming Packages"]
        direction TB
        SRV["@memberjunction/server"]
        CG["@memberjunction/codegen-lib"]
        MCP["@memberjunction/mcp-server"]
        A2A["@memberjunction/a2a-server"]
        CLI["@memberjunction/cli"]
        MS["@memberjunction/metadata-sync"]
    end

    Consumers --> ConfigPackage

    style ConfigPackage fill:#2d6a9f,stroke:#1a4971,color:#fff
    style CL fill:#7c5295,stroke:#563a6b,color:#fff
    style CM fill:#7c5295,stroke:#563a6b,color:#fff
    style CT fill:#7c5295,stroke:#563a6b,color:#fff
    style EU fill:#7c5295,stroke:#563a6b,color:#fff
    style Consumers fill:#2d8659,stroke:#1a5c3a,color:#fff
    style SRV fill:#2d8659,stroke:#1a5c3a,color:#fff
    style CG fill:#2d8659,stroke:#1a5c3a,color:#fff
    style MCP fill:#2d8659,stroke:#1a5c3a,color:#fff
    style A2A fill:#2d8659,stroke:#1a5c3a,color:#fff
    style CLI fill:#2d8659,stroke:#1a5c3a,color:#fff
    style MS fill:#2d8659,stroke:#1a5c3a,color:#fff
PackagePurpose
cosmiconfigConfiguration file discovery and loading
lodash.mergewithDeep merge with custom merge strategy
zodSchema validation (available for consumers)
PackageRelationship
@memberjunction/serverDefines MJServerConfig defaults; uses mergeConfigs and parseBooleanEnv
@memberjunction/codegen-libDefines CodeGenConfig defaults; uses mergeConfigs and parseBooleanEnv
@memberjunction/mcp-serverDefines MCPServerConfig defaults; uses mergeConfigs
@memberjunction/a2a-serverDefines A2AServerConfig defaults; uses mergeConfigs
@memberjunction/cliCLI tool config loading; uses mergeConfigs and parseBooleanEnv
@memberjunction/metadata-syncMetadata sync config; uses mergeConfigs and parseBooleanEnv