npm package: @mightyacorndigital/config
Description: A configuration management library for TypeScript projects.
Author: Mighty Acorn Digital
Latest version: 1.0.1

# @mightyacorndigital/config

A lightweight TypeScript configuration fetching and validation library built on [Zod](https://zod.dev). Provides type-safe configuration resolution from arbitrary sources (environment variables, AWS Secrets Manager, etc.) with runtime validation.

## Features

- **Flexible sources** - Use static objects, accessor functions, or async sources (Promises)
- **Type-safe** - Full TypeScript support with inferred types from your Zod schema
- **Lazy resolution** - Configuration values are validated on access, not at startup
- **Bulk validation** - Validate all configuration at once to catch errors early
- **CLI tools** - Validate configuration and generate `.env` templates from schemas

## Installation

```bash
npm install @mightyacorndigital/config zod
```

## Usage

### Define a Schema

```typescript
// config/schema.ts
import { z } from "zod";

const schema = z.object({
  DATABASE_URL: z.url().meta({
    title: "Database URL",
    description: "PostgreSQL connection string",
    examples: ["postgres://user:password@localhost:5432/mydb"],
  }),
  API_KEY: z.string(),
  PORT: z.coerce.number().default(3000),
});

export default schema;
```

### Synchronous Resolution

Use `ConfigResolver` when your configuration source is synchronous (e.g., environment variables):

```typescript
import { ConfigResolver } from "@mightyacorndigital/config";
import schema from "./config/schema";

const config = new ConfigResolver(schema, process.env);

// Get individual values (validated on access)
const dbUrl = config.get("DATABASE_URL"); // string (validated URL)
const port = config.get("PORT"); // number

// Validate all configuration at once
const errors = config.validate();
if (errors) {
  console.error("Configuration errors:", errors);
  process.exit(1);
}
```

### Asynchronous Resolution

Use `AsyncConfigResolver` when fetching configuration from async sources like AWS Secrets Manager:

```typescript
import { AsyncConfigResolver } from "@mightyacorndigital/config";
import { SecretsManager } from "@aws-sdk/client-secrets-manager";

const secretsManager = new SecretsManager();

const config = new AsyncConfigResolver(schema, async (key) => {
  const secret = await secretsManager.getSecretValue({ SecretId: "my-app-config" });
  return JSON.parse(secret.SecretString!);
});

// Get values asynchronously
const dbUrl = await config.get("DATABASE_URL");

// Validate all configuration
const errors = await config.validate();
```

### Source Types

Both resolvers accept flexible source types:

```typescript
// Static object
new ConfigResolver(schema, process.env);
new ConfigResolver(schema, { DATABASE_URL: "...", API_KEY: "..." });

// Accessor function (called per-key)
new ConfigResolver(schema, (key) => myConfigStore.get(key));

// Async sources
new AsyncConfigResolver(schema, fetchConfigFromRemote());
new AsyncConfigResolver(schema, async (key) => fetchSecret(key));
```

## CLI

The package includes a CLI for common configuration tasks.

### Validate Configuration

Validate a configuration file that exports a `ConfigResolver` or `AsyncConfigResolver`:

```bash
npx config-cli validate ./config.js
```

The config file should have a default export:

```typescript
// config.js
import { ConfigResolver } from "@mightyacorndigital/config";
import schema from "./schema.js";

export default new ConfigResolver(schema, process.env);
```

### Generate .env Template

Generate a `.env` template file from a Zod schema:

```bash
npx config-cli generate-env ./schema.js > .env.example
```

The schema file should have a default export:

```typescript
// schema.js
import { z } from "zod";

export default z.object({
  DATABASE_URL: z.url().meta({
    title: "Database URL",
    description: "PostgreSQL connection string",
    examples: ["postgres://localhost:5432/mydb"],
  }),
  API_KEY: z.string().meta({
    title: "API Key",
  }),
  DEBUG: z.boolean().default(false),
});
```

Output:

```env
# Database URL
# PostgreSQL connection string
DATABASE_URL=postgres://localhost:5432/mydb
# API Key
API_KEY=your_value_here
# DEBUG=false # default value
```

## Error Handling

The library provides typed errors for different failure scenarios:

- `InvalidConfigKeyError` - Requested key not in schema
- `ConfigValidationError` - Value failed Zod validation
- `ConfigSourceError` - Source function threw an error

All errors extend `ConfigError` and preserve the original cause for debugging.

## License

MIT