Skip to content

@studnicky/config

Configuration parsing, errors, and clamping utilities.

Install

bash
pnpm add @studnicky/config

Requires @studnicky:registry=https://npm.pkg.github.com in .npmrc.

Usage

Parse external configuration through an entity's intake function. Intake supplies schema defaults and removes undeclared properties, without coercing a value's type — a wrong-typed field is rejected, not silently converted:

ts
namespace ServerConfigEntity {
  export const Schema = {
    'additionalProperties': false,
    'properties': {
      'debug': { 'default': false, 'type': 'boolean' },
      'host': { 'minLength': 1, 'type': 'string' },
      'maximumRetries': { 'default': 3, 'minimum': 0, 'type': 'integer' },
      'port': { 'default': 8080, 'maximum': 65_535, 'minimum': 1, 'type': 'integer' }
    },
    'required': ['host'],
    'type': 'object'
  } as const satisfies JSONSchema;

  export type Type = FromSchema<typeof Schema>;

  export const validate: ValidateFunction<Type> = SchemaValidator.compile<Type>(Schema);
  export const intake: SchemaIntakeFunctionInterface<Type> = SchemaValidator.compileIntake<Type>(Schema);
  export const create: SchemaCreateFunctionInterface<Type> = SchemaValidator.compileCreate<Type>(Schema);
}

const config = ServerConfigEntity.intake({ 'host': 'localhost', 'ignored': true, 'port': 8081 });

console.log('Parsed config:', config);

assert.deepEqual(config, {
  'debug': false,
  'host': 'localhost',
  'maximumRetries': 3,
  'port': 8081
});

const localConfig = ServerConfigEntity.create({ 'host': 'test.local' });
assert.deepEqual(localConfig, {
  'debug': false,
  'host': 'test.local',
  'maximumRetries': 3,
  'port': 8080
});

Public API

Import ClampedConfig and ConfigurationError from @studnicky/config; import clamping schemas from @studnicky/config/entities.

Try it

Loading example…

The output shows a typed configuration with defaults applied and undeclared properties removed.

Configuration errors

Build a ConfigurationError with an Error cause when an already-parsed configuration cannot be used:

ts
import { ConfigurationError } from '../src/index.js';

const cause = RuntimeError.create('environment variable CONFIG_URL is not set');
const configurationError = ConfigurationError.create('configuration is invalid', cause);

console.log('Configuration error:', configurationError.message);
console.log('Cause:', configurationError.cause);

Clamping

ClampedConfig applies declarative {min, max, reason} rules to a flat configuration object. apply returns a new object with out-of-range numeric fields clamped into range. Fields not present in the rule table, not numeric, or already in range are copied through unchanged; the input is never mutated.

ts
import { ClampedConfig } from '@studnicky/config';
import { ClampRuleEntity } from '@studnicky/config/entities';

interface WorkerConfig {
  timeoutMs: number;
  concurrency: number;
}

const rules: Record<string, ClampRuleEntity.Type> = {
  timeoutMs: { min: 100, max: 5000, reason: 'timeout must stay within safe bounds' },
  concurrency: { min: 1, max: 8, reason: 'concurrency must stay within pool capacity' },
};

const raw: WorkerConfig = { timeoutMs: 10, concurrency: 4 };
const clamped = ClampedConfig.apply(raw, rules);
// clamped.timeoutMs === 100, clamped.concurrency === 4, raw is unchanged

Override the protected onClamp static method to observe clamp events — logging is the caller's responsibility, ClampedConfig has no dependency on any logging package:

ts
import { ClampedConfig } from '@studnicky/config';
import { ClampEventEntity } from '@studnicky/config/entities';

class LoggingClampedConfig extends ClampedConfig {
  protected static override onClamp(event: ClampEventEntity.Type): void {
    console.warn(`[config] clamped ${event.field}: ${event.raw} -> ${event.clamped} (${event.reason})`);
  }
}

LoggingClampedConfig.apply(raw, rules);

Entities

@studnicky/config/entities exports clamping rule and event schemas.

typescript
import { ClampRuleEntity } from '@studnicky/config/entities';

Exports

SymbolPurposeImport path
ClampedConfigApplies declarative numeric clamping rules.@studnicky/config
ConfigurationErrorRepresents invalid configuration values.@studnicky/config

Source on GitHub