import {Meta} from '@storybook/addon-docs/blocks'

<Meta title='@dxtmisha/ru/functional-basic/Classes/ServerStorage - Изоляция данных в SSR'/>

# Класс ServerStorage

`ServerStorage` — это механизм для управления **изолированным глобальным состоянием** в приложениях с поддержкой SSR (Server-Side Rendering).

Класс позволяет работать с данными так же просто, как со статическими переменными, но гарантирует, что на сервере данные разных пользователей не будут смешиваться.

## Основные возможности

- **Изоляция запросов** — на сервере данные привязываются к контексту текущего HTTP-запроса.
- **Гидратация** — возможность автоматически передавать состояние с сервера на клиент через JSON.
- **Безопасность** — автоматическое экранирование XSS при передаче данных.

```typescript
import { ServerStorage } from '@dxtmisha/functional-basic'

// Сохранение данных с флагом гидратации (будут доступны на клиенте)
ServerStorage.set('theme', () => 'dark', true)

// Получение данных
const theme = ServerStorage.get('theme') // 'dark'
```

## Методы доступа к данным

### `get`

Извлекает значение по ключу. Если оно не существует, создает его с помощью фабрики значений по умолчанию.

**Параметры:**
- `key: string` — уникальный ключ хранилища.
- `defaultValue?: () => T` — функция, возвращающая значение по умолчанию, если оно не найдено.
- `hydration: boolean` — если `true`, значение будет включено в JSON для передачи на клиент (по умолчанию: `false`).

**Возвращает:** `T`

### `set`

Записывает значение в хранилище.

**Параметры:**
- `key: string` — уникальный ключ хранилища.
- `value: () => T` — функция, возвращающая значение для сохранения.
- `hydration: boolean` — если `true`, значение будет включено в JSON для передачи на клиент (по умолчанию: `false`).

**Возвращает:** `T`

## Вспомогательные методы и жизненный цикл

- `init(listener: () => Record<string, any>): typeof ServerStorage` — Инициализирует хранилище слушателем контекста. На сервере этот слушатель должен возвращать объект, уникальный для каждого запроса.
- `has(key: string): boolean` — Проверяет наличие ключа в текущем контексте.
- `remove(key: string): void` — Удаляет значение по ключу из хранилища.
- `setErrorStatus(hide: boolean): void` — Устанавливает, нужно ли скрывать ошибки отсутствия контекста в консоли/центре ошибок.
- `reset(): void` — Сбрасывает хранилище, очищая все сохраненные данные и слушателя. Полезно для выполнения очистки.
- `toString(): string` — Генерирует HTML-тег `<script type="application/json">` со всеми данными, помеченными для гидратации.

## Изоляция в SSR

В обычных клиентских приложениях глобальное состояние принадлежит одному пользователю. Однако в среде SSR один процесс Node.js обрабатывает множество одновременных запросов. Чтобы данные пользователей не смешивались между собой (Загрязнение состояния), `ServerStorage` должен быть правильно изолирован.

Для этого необходимо инициализировать `ServerStorage` с помощью метода `init`. Он принимает функцию-слушатель, которая возвращает объект данных, уникальный для контекста текущего запроса.

> Важно: Правильная изоляция критична для безопасности. Без нее данные одного пользователя могут быть по ошибке переданы другому.

### Node.js (AsyncLocalStorage)

Рекомендуемый способ для Node.js — использование `AsyncLocalStorage` для сохранения контекста на протяжении асинхронного запроса.

```typescript
import { AsyncLocalStorage } from 'node:async_hooks'
import { ServerStorage } from '@dxtmisha/functional-basic'

const als = new AsyncLocalStorage<Record<string, any>>()

// Инициализация
ServerStorage.init(() => als.getStore() || {})

// Использование на сервере
http.createServer((req, res) => {
  als.run({}, () => {
    // Внутри этого блока все вызовы ServerStorage будут изолированы
    ServerStorage.set('requestId', () => req.id)
  })
})
```

### Nuxt / Nitro

В Nuxt/Nitro изоляция управляется через контекст события `h3` с помощью `useEvent()` внутри серверного плагина.

```typescript
// server/plugins/server-storage.ts
import { ServerStorage } from '@dxtmisha/functional-basic'

export default defineNitroPlugin(() => {
  ServerStorage.init(() => {
    try {
      return useEvent().context
    } catch {
      return {}
    }
  })
})
```

## Гидратация (Передача данных клиенту)

Гидратация — это процесс передачи состояния приложения, установленного на сервере, клиенту. Это позволяет браузеру «подхватить» данные там, где закончил сервер, обеспечивая согласованность и предотвращая повторные запросы данных или «мерцание» страницы при загрузке.

### 1. Пометка данных для гидратации

При использовании `set()` или `get()` передайте `true` третьим параметром (`hydration`). Только значения, помеченные таким образом, будут переданы на клиент.

```typescript
// Установка на сервере
ServerStorage.set('userSettings', () => ({ fontSize: 16 }), true)
```

### 2. Вставка состояния в HTML

На сервере необходимо вызвать `ServerStorage.toString()` и вставить результат в шаблон страницы (обычно в самый конец тега `<body>`, перед скриптами приложения).

Этот метод генерирует специальный тег script:
```html
<script id="__ui:server:storage:id__" type="application/json">
  {"userSettings":{"fontSize":16}}
</script>
```
