npm package: @liahus/safe-env
Description: Type-safe environment variable validation for TypeScript & JavaScript. Framework-agnostic, validates at startup, secrets masked, DoS protected.
Author: SUHAIL
Latest version: 1.0.3

<div align="center">

# 🔒 safe-env

**Type-safe environment variable validation for TypeScript & JavaScript**

[![npm version](https://img.shields.io/npm/v/@liahus/safe-env?style=flat-square)](https://www.npmjs.com/package/@liahus/safe-env)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT)
[![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue?style=flat-square)](https://www.typescriptlang.org/)
[![Bundle Size](https://img.shields.io/badge/bundle%20size-6.8KB-green?style=flat-square)](https://bundlephobia.com/package/@liahus/safe-env)

> **Framework-agnostic • Validates at startup • Secrets masked • DoS protected**

[Installation](#-installation) • [Quick Start](#-quick-start) • [Documentation](#-documentation) • [Security](#-security) • [Contributing](#-contributing)

</div>

---

## 🚀 Installation

```bash
npm install @liahus/safe-env
```

**That's it!** No configuration needed. Works in Node.js, Bun, Deno, Next.js, Express, and more.

---

## ⚡ Quick Start

```typescript
import { env } from "@liahus/safe-env";

const ENV = env({
  DATABASE_URL: "string:url",
  PORT: "number:default=3000",
  NODE_ENV: "enum:development,production,test",
  JWT_SECRET: "string:min=32:secret",
});

// ENV is fully typed! 🎉
console.log(ENV.PORT); // 3000 (number, not string!)
console.log(ENV.NODE_ENV); // "development" | "production" | "test"
```

**Works in JavaScript too!** Type inference is optional.

```javascript
import { env } from "@liahus/safe-env";

const ENV = env({
  PORT: "number:default=3000",
  API_URL: "string:url",
});

console.log(ENV.PORT); // 3000 (coerced to number)
```

---

## ✨ Features

- ✅ **Type-safe** - Numbers are numbers, enums are union types
- ✅ **Framework-agnostic** - Works in Node.js, Bun, Deno, Next.js, Express, Fastify, NestJS, Vite, Webpack
- ✅ **Validates at startup** - Fail fast with clear errors
- ✅ **Secret masking** - Secrets never leak in error messages
- ✅ **DoS protected** - Built-in input validation limits
- ✅ **Zero config** - No filesystem access, no `.env` loading
- ✅ **Simple DSL** - `"string:url"`, `"number:default=3000"`, `"enum:dev,prod"`

---

## 📖 DSL Syntax

### Types

| Type | Description | Example |
|------|-------------|---------|
| `string` | String value | `"string"` |
| `number` | Number (auto-coerced) | `"number"` |
| `enum` | Enum with values | `"enum:dev,prod,staging"` |

### Modifiers

| Modifier | Applies To | Description | Example |
|----------|------------|-------------|---------|
| `url` | `string` | Validates URL format | `"string:url"` |
| `min=N` | `string`, `number` | Minimum length/value | `"string:min=10"`, `"number:min=1000"` |
| `default=value` | All | Default if missing | `"string:default=info"` |
| `secret` | All | Masks in errors | `"string:secret"` |

### Examples

```typescript
env({
  // String with URL validation
  API_URL: "string:url",
  
  // String with minimum length and secret masking
  API_KEY: "string:min=32:secret",
  
  // Number with default value
  PORT: "number:default=3000",
  
  // Number with minimum value
  TIMEOUT: "number:min=1000",
  
  // Enum with specific values
  NODE_ENV: "enum:development,production,test",
  
  // String with default
  LOG_LEVEL: "string:default=info",
});
```

---

## 🛡️ Security

### Secret Protection

Secrets are **fully masked** in error messages - only length is shown:

```typescript
env({
  JWT_SECRET: "string:min=32:secret",
});

// Error: [REDACTED (length: 12)]
// Never exposes partial values or actual content
```

### DoS Protection

Built-in limits prevent resource exhaustion:
- DSL strings: Max 1000 characters
- Environment variables: Max 1000 per schema
- Enum values: Max 100 values, 200 chars each

### Best Practices

```typescript
// ✅ Always mark sensitive values as secret
env({
  JWT_SECRET: "string:min=32:secret",
  API_KEY: "string:min=20:secret",
  DATABASE_URL: "string:url:secret",
});

// ❌ Don't log environment variables
console.log("API Key:", ENV.API_KEY); // Bad!

// ✅ Check existence instead
console.log("API Key configured:", !!ENV.API_KEY);
```

---

## 🎨 Examples

### Next.js

```typescript
// app/config/env.ts
import { env } from "@liahus/safe-env";

export const ENV = env({
  DATABASE_URL: "string:url",
  NEXTAUTH_SECRET: "string:min=32:secret",
  NEXTAUTH_URL: "string:url",
});
```

### Express / Fastify

```typescript
// config/env.ts
import { env } from "@liahus/safe-env";

export const ENV = env({
  PORT: "number:default=3000",
  DATABASE_URL: "string:url",
  JWT_SECRET: "string:min=32:secret",
});

// server.ts
import { ENV } from "./config/env";
app.listen(ENV.PORT);
```

### Custom Environment Source

```typescript
import { env } from "@liahus/safe-env";

// Inject custom environment (useful for testing)
const ENV = env(
  {
    API_KEY: "string:min=10:secret",
    TIMEOUT: "number:default=5000",
  },
  {
    env: {
      API_KEY: "my-secret-key",
      // TIMEOUT will use default: 5000
    },
  }
);
```

---

## 🛡️ Error Handling

Clean, human-readable errors with **automatic secret masking**:

```typescript
import { env, EnvValidationError } from "@liahus/safe-env";

try {
  const ENV = env({
    DATABASE_URL: "string:url",
    PORT: "number",
    JWT_SECRET: "string:min=32:secret",
  });
} catch (error) {
  if (error instanceof EnvValidationError) {
    console.error(error.message);
    // Environment variable validation failed:
    //   DATABASE_URL: Invalid url (received: undefined)
    //   PORT: Required (received: undefined)
    //   JWT_SECRET: String must contain at least 32 character(s) (received: [REDACTED (length: 12)])
    
    // Access individual errors (no secrets exposed)
    error.errors.forEach((err) => {
      console.log(`${err.key}: ${err.message}`);
    });
  }
}
```

---

## 📚 API Reference

### `env(schema, options?)`

Validates and returns typed environment variables.

**Parameters:**
- `schema` - Object mapping variable names to DSL strings
- `options.env` - Optional custom environment source (defaults to `process.env` if available)

**Returns:**
- Fully typed object matching your schema

**Throws:**
- `EnvValidationError` - When validation fails

### `EnvValidationError`

Custom error class for validation failures.

**Properties:**
- `message` - Human-readable error message
- `errors` - Array of `{ key: string, message: string }` objects

---

## 🔧 Advanced

### With dotenv

```typescript
import "dotenv/config"; // Load .env file
import { env } from "@liahus/safe-env";

export const ENV = env({
  DATABASE_URL: "string:url",
});
```

### Type Inference

Full TypeScript type inference from your schema:

```typescript
const ENV = env({
  PORT: "number:default=3000",
  NODE_ENV: "enum:development,production",
});

// ENV.PORT is number
// ENV.NODE_ENV is "development" | "production"
```

---

## 🏗️ Development

### Prerequisites

- Node.js 18+ or Bun
- npm or yarn

### Setup

```bash
# Clone the repository
git clone https://github.com/suhailopensource/safe-env.git
cd safe-env

# Install dependencies
npm install

# Build
npm run build
```

### Project Structure

```
safe-env/
├── src/
│   ├── index.ts      # Main entry point
│   ├── dsl.ts        # DSL parser
│   ├── schema.ts     # Zod schema builder
│   └── errors.ts     # Error handling
├── dist/             # Compiled output (generated)
├── test.ts           # Test suite
├── package.json
└── README.md
```

### Running Tests

```bash
npx tsx test.ts
```

### Building

```bash
npm run build
```

This generates:
- `dist/index.js` (ESM)
- `dist/index.cjs` (CommonJS)
- `dist/index.d.ts` (TypeScript definitions)

---

## 🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

### Development Guidelines

- Follow TypeScript best practices
- Add tests for new features
- Update documentation as needed
- Ensure all tests pass before submitting

---

## 📝 License

MIT © SUHAIL

See [LICENSE](LICENSE) for more information.

---

## 🙏 Acknowledgments

- Built with [Zod](https://github.com/colinhacks/zod) for validation
- Inspired by the need for type-safe environment variable handling

---

<div align="center">

**Made with ❤️ for developers who care about type safety**

[Report Bug](https://github.com/suhailopensource/safe-env/issues) · [Request Feature](https://github.com/suhailopensource/safe-env/issues) · [View on npm](https://www.npmjs.com/package/@liahus/safe-env)

</div>