npm package: @mirta/env-loader
Description: Internal dotenvx-based environment loader, used by Mirta Framework
Latest version: 0.5.2

# `@mirta/env-loader`

[![en](https://img.shields.io/badge/lang-en-dimgray.svg?style=flat-square)](https://github.com/wb-mirta/core/blob/latest/packages/mirta-env-loader/README.md)
[![ru](https://img.shields.io/badge/lang-ru-olivedrab.svg?style=flat-square)](https://github.com/wb-mirta/core/blob/latest/packages/mirta-env-loader/README.ru.md)
[![NPM Version](https://img.shields.io/npm/v/@mirta/env-loader?style=flat-square)](https://npmjs.com/package/@mirta/env-loader)
[![NPM Downloads](https://img.shields.io/npm/dm/@mirta/env-loader?style=flat-square&logo=npm)](https://npmjs.com/package/@mirta/env-loader)

> Внутренний загрузчик переменных окружения на базе `@dotenvx/dotenvx`, используемый инструментами Mirta.

`@mirta/env-loader` обеспечивает единый способ загрузки и фильтрации переменных окружения в рамках фреймворка Mirta.

Поддерживает:
- `.env`-файлы с режимами (`development`, `test`, `production`) и `.local`-файлами
- Переменные из операционной системы и CLI-переопределения
- Фильтрацию по префиксам (`MIRTA_`, `APP_`)
- Шифрование `.env`-файлов через `@dotenvx/dotenvx`

Используется в `@mirta/rollup`, `@mirta/testing` и других внутренних инструментах.

**Не предназначен для выполнения в среде Duktape на контроллерах Wiren Board.**

## 📦 Установка

```bash
# Не требуется напрямую — используется внутри Mirta
pnpm add -D @mirta/env-loader
```

⚠️ Этот пакет — часть внутренней инфраструктуры фреймворка Mirta. Обычно он не используется напрямую.

## 🚀 Быстрый старт

```ts
import { loadEnv, loadEnvReplacements } from '@mirta/env-loader'

// Загрузка переменных окружения
const env = loadEnv({ mode: 'development' })

// Для подстановки в код (например, @rollup/plugin-replace)
const replacements = loadEnvReplacements({ mode: 'production' })
```

## 🧰 API

### `loadEnv(options?: EnvLoaderOptions): Record<string, string>`
Синхронно загружает и фильтрует переменные окружения.

#### Параметры

| Поле | Тип | Описание |
|------|-----|----------|
| `mode` | `string` | Режим окружения. По умолчанию — `process.env.NODE_ENV` |
| `prefix` | `string \| string[]` | Префиксы для фильтрации. По умолчанию — `['MIRTA_', 'APP_']` |
| `cwd` | `string` | Текущая рабочая директория. По умолчанию — `process.cwd()` |
| `rootDir` | `string` | Корневая директория проекта (например, в монорепозитории).<br/>Если значение указано и отличается от `cwd`, файлы ищутся также и в корне |
| `envFile` | `string \| string[]` | Базовое имя `.env`-файла. По умолчанию — `.env` |
| `keepNodeEnv` | `boolean` | Сохранять ли `NODE_ENV`. По умолчанию — `true`, при `false` удаляется из возвращаемого объекта.<br/>⚠️ Примечание: не влияет на глобальное значение `process.env.NODE_ENV` |
| `dotenv` | `DotenvOptions` | Дополнительные настройки `@dotenvx/dotenvx`, см. ниже |

#### ⚠️ **Безопасность: `prefix` нельзя отключить полностью**

Параметр `prefix` **не может быть отключён** (например, через `prefix: false`, `prefix: ''`, `prefix: []` или `prefix: ['']`), чтобы предотвратить **автоматическую утечку секретов**.

Даже при загрузке всех переменных, фильтрация остаётся включённой, и применяются префиксы по умолчанию (`MIRTA_`, `APP_`).

Если вам нужно загрузить переменные с другими префиксами — явно укажите их:

```ts
loadEnv({
  prefix: ['MIRTA_', 'APP_', 'CUSTOM_', 'SECRET_'] // ← явное разрешение
})
```
Это гарантирует, что **только ожидаемые переменные** попадут в результат и, при использовании `loadEnvReplacements`, — в клиентский код.

#### ⚙️ **Опции dotenv****

Тип: `DotenvOptions` — передаётся напрямую в `@dotenvx/dotenvx`, но с ограничениями.

`@mirta/env-loader` контролирует ключевые аспекты загрузки, поэтому некоторые опции переопределяются или игнорируются, чтобы гарантировать предсказуемое поведение.

✅ **Поддерживаемые и безопасные опции**

| Опция | Описание |
|------|---------|
| `overload` | Если `true` — последующие файлы перезаписывают переменные из предыдущих. По умолчанию `false` (приоритет у первых файлов) |
| `encoding` | Кодировка `.env`-файлов. По умолчанию `'utf8'` |
| `strict` | Если `true` — выбрасывает ошибку при сбоях парсинга. По умолчанию `false` |
| `debug` | Включает отладочный вывод. Полезно для диагностики. По умолчанию `false` |
| `verbose` | Увеличивает детализацию логов |
| `quiet` | Подавляет вывод в консоль (включая ошибки) |
| `envKeysFile` | Путь к `.env.keys` — полезно в монорепозиториях |
| `logLevel` | Уровень логирования: `'error'`, `'warn'`, `'info'` и т.д. Частично переопределён — см. ниже |

❌ **Опции, которые игнорируются или переопределяются**

| Опция | Что происходит | Причина |
|------|----------------|--------|
| `path` | Игнорируется | Порядок и список файлов задаётся `@mirta/env-loader` |
| `processEnv` | Игнорируется | Внутренний объект, чтобы не загрязнять `process.env` и применить фильтрацию |
| `convention` | Игнорируется | Чтобы избежать конфликта с собственной логикой приоритетов |
| `logLevel` | По умолчанию `'warn'`, но может быть переопределён | Чтобы не подавлять важные предупреждения, например, о пропущенных `.env.keys` |
| `ignore` | Принудительно включает `'MISSING_ENV_FILE'` | `.env`-файлы не обязательны — отсутствие не является ошибкой |

📌 **Рекомендации**

- Используйте `overload`, `debug`, `encoding` — они работают как ожидается,
- Не полагайтесь на `convention` или `path` — они отключены,
- Если вы хотите изменить порядок загрузки — настройте envFile, mode и структуру `.env`-файлов.

Пример

```ts
const env = loadEnv({
  mode: 'production',
  dotenv: {
    overload: true,     // ✅ разрешено: инвертирует приоритет обработки файлов
    debug: true,        // ✅ разрешено: вывод в консоль
    encoding: 'utf16',  // ✅ редко, но возможно
    // convention: 'nextjs'  // ❌ проигнорировано
  }
})
```

### Порядок загрузки файлов

Файлы обрабатываются в порядке убывания приоритета:

1. **Сначала — все `.env`-файлы в `cwd`** (текущей директории):
   - `.env.${mode}.local`
   - `.env.${mode}`
   - `.env.local`
   - `.env`

2. **Затем — все `.env`-файлы в `rootDir`** (корне проекта), в том же порядке.

#### ⚠️ **Локальные настройки пакета имеют приоритет над корневыми**

Предположим, что:
- некий разрабатываемый проект собирается в режиме `development`,
- файл пакета `packages/my-app/.env` содержит `PORT=3000`,
- корневой файл проекта `.env.development` содержит `PORT=4000`.

В этом случае будет использовано значение `PORT=3000`,<br/>
потому что локальный контекст считается более специфичным.

#### ⚠️ **Поведение при совпадении ключей зависит от значения `dotenv.overload`**

- **`dotenv.overload`: `false`** (по умолчанию)

  Переменные из **первых** файлов **не перезаписываются** последующими.<br/>
  → Чем **раньше** файл в списке — тем **выше** его приоритет.

- **`dotenv.overload`: `true`**

  Последующие файлы **перезаписывают** предыдущие.<br/>
  → Чем **позже** файл в списке — тем **выше** его приоритет.

По умолчанию используется `overload`: `false`, поэтому `.env.${mode}.local` имеет наивысший приоритет.

---

### `loadEnvReplacements(options?: EnvLoaderOptions): Record<string, string>`

Возвращает объект вида:

```ts
{
  'process.env.APP_PORT': '"3000"',
  'import.meta.env.APP_PORT': '"3000"'
}
```
Подходит для интеграции с `@rollup/plugin-replace`

---

### `DEFAULT_ENV_PREFIXES`

```ts
['MIRTA_', 'APP_']
```
Список префиксов по умолчанию для фильтрации переменных.
Можно переопределить через `options.prefix`.

## 🛡️ Рекомендации по безопасности и проектированию

### ❌ Не используйте системные переменные напрямую

Прямая загрузка системных переменных окружения — таких как `PATH`, `HOME`, `USER`, `NODE_VERSION`, **не рекомендуется**. Даже если они доступны в среде выполнения.

👉 Почему:
- Это **нарушает переносимость**: поведение будет отличаться на разных машинах.
- Это **риск безопасности**: в сборку может случайно попасть приватная информация.
- Это **нарушает детерминированность**: поведение приложения зависит от окружения, а не от конфигурации.

---

### ✅ Вариант 1: Явное переопределение в `.env`

Если вам действительно нужно значение системной переменной — **явно объявите его в `.env`-файле**:

```env
# .env
MIRTA_PATH=`${PATH}`
MIRTA_HOME=`${HOME}`
APP_NODE_VERSION=`${NODE_VERSION}`
```
👉 Так:
- Вы **контролируете**, какие переменные попадают в приложение,
- Вы **логируете** это намерение явно,
- Вы можете **переопределить** значение для конкретного окружения.

⚠️ Важно: `@mirta/env-loader` не раскрывает переменные вне указанных префиксов — даже если `${PATH}` используется в `.env`, сам `PATH` не попадёт в результат, если не начинается с `MIRTA_` или `APP_`.

---

### ✅ Вариант 2: Явная конфигурация (рекомендуется)

Лучше — вообще не полагаться на системные переменные, а задавать пути и настройки явно:

```env
# .env
MIRTA_BINARY_PATH=/usr/local/bin/custom-tool
APP_CONFIG_DIR=/etc/myapp
APP_CACHE_DIR=./temp/cache
```

👉 Преимущества:
- **Переносимость**: приложение работает одинаково на всех машинах,
- **Читаемость**: все настройки видны в `.env`,
- **Безопасность**: никаких сюрпризов из окружения,
- **Тестируемость**: легко эмулировать разные окружения.

---

🧩 Пример безопасного `.env`

```env
# Режим окружения
NODE_ENV=development

# Пути и бинарники
MIRTA_BINARY_PATH=/opt/tools/cli
MIRTA_CONFIG_ROOT=./config

# Пользовательские настройки
APP_PORT=3000
APP_API_KEY=dev-key-7x29qn
APP_FEATURE_TOGGLES=auth,logging,metrics

# Логирование
MIRTA_LOG_LEVEL=debug
```

📌 Итог
- ❌ **Не полагайтесь** на системные переменные напрямую.
- ✅ **Явно объявляйте** всё, что нужно, в .env.
- ✅ **Используйте понятные, зафиксированные значения**.

Так вы строите **предсказуемое, безопасное и переносимое** приложение.

## 🔐 Работа с зашифрованными переменными

`@mirta/env-loader` использует `@dotenvx/dotenvx` для загрузки и расшифровки `.env`-файлов.

> ⚠️ **Устарело: `DOTENV_KEY` и `.env.vault`**
>
> Старый механизм шифрования через `DOTENV_KEY` и `.env.vault` **устарел**.  
> Поддерживается только для обратной совместимости. Не рекомендуется к использованию.

✅ **Новый механизм: шифрование с парой ключей**

`dotenvx` теперь использует асимметричное шифрование:
- **`DOTENV_PUBLIC_KEY`** — для **шифрования** `.env`-файлов.
- **`DOTENV_PRIVATE_KEY`** — для **расшифровки** в рантайме.

При использовании:
- `.env`-файлы **полностью зашифрованы на диске**.
- Публичный ключ можно фиксировать в репозитории.
- Приватный ключ остаётся в CI/CD или локальном окружении.

🔍 Как это работает:
- `@dotenvx/dotenvx` выполняет все криптографические операции.
- `@mirta/env-loader` получает **уже расшифрованные значения** — не работает с ключами напрямую.

👉 Рекомендуется перейти на новую систему.  
Подробнее: [dotenvx — Encryption Guide](https://github.com/dotenvx/dotenvx#encryption)

## ✅ Тестирование

Пакет покрыт юнит-тестами (Vitest):

- Загрузка `.env`-файлов в правильном порядке
- Фильтрация по префиксам
- Поддержка `rootDir`
- Интеграция с `DOTENV_KEY` (через моки)
- Кроссплатформенность

## ⚠️ Ограничения

**Работает только в Node.js** (не в Duktape).<br/>