# Хранилище

Как пользоваться хранилищами — в руководстве [Общие данные](https://dev.pairx.org/guides/storage). Здесь — точные формы.

## Снимок {#snapshot}

`await pairx.call("storage.snapshot")`:

```js
{
  seq: 42,                 // номер последнего изменения в снимке
  private: { … },          // личные данные — их отдаёт localStorage
  stores: {
    queue: { type: "log", entries: [{ entry: 7, user: "…", value: "join", at: 1760000000, retracted: false }] },
    votes: { type: "slot", slots: { "<user>": "Кафе" } },
    board: { type: "doc", value: { … }, version: 3 },
    likes: { type: "counter", totals: { "7": 12 }, mine: { "7": 1 } }
  }
}
```

`at` — секунды Unix по часам сервера. Документ, в который ещё не писали, — `{ value: null, version: 0 }`.

## Операции {#ops}

`await pairx.call("storage.write", { op, store, ...поля })`:

| Операция | Поля | Ответ | Изменение |
| --- | --- | --- | --- |
| `log.append` | `value` | `{ entry, at, seq }` | `{ op: "log", entry, user, value, at, seq, store }` |
| `log.retract` | `entry` | `{ seq }` | `{ op: "retract", entry, seq, store }` |
| `slot.set` | `value` | `{ seq }` | `{ op: "slot", user, value, seq, store }` |
| `slot.clear` | — | `{ seq }` | `{ op: "slot", user, value: null, seq, store }` |
| `doc.set` | `value`, `version` | `{ version, seq }` | `{ op: "doc", value, version, by, seq, store }` |
| `counter.add` | `key`, `amount` | `{ total, mine, seq }` | `{ op: "counter", key, total, user, mine, seq, store }` |

Каждая запись называет `store` — имя хранилища из манифеста.

## Изменения {#changes}

Событие `storage.changed` приносит `{ changes: [...] }` — изменения по порядку `seq`. У каждого есть `seq`, `store` и `op`; остальные поля зависят от операции (колонка «Изменение» выше).

```js
pairx.on("storage.changed", ({ changes }) => {
  for (const c of changes) {
    switch (c.op) {
      case "log":     /* c.entry, c.user, c.value, c.at */ break;
      case "retract": /* c.entry */ break;
      case "slot":    /* c.user, c.value (null — очищено) */ break;
      case "doc":     /* c.value, c.version, c.by */ break;
      case "counter": /* c.key, c.total, c.user, c.mine */ break;
    }
  }
});
```

Событие `storage.reset` — изменений больше нет в ленте или кто-то удалил свои данные: перечитайте снимок.

## Права {#writers}

| `writers` | Кто пишет |
| --- | --- |
| `everyone` | Все участники (по умолчанию). |
| `admins` | Тот, кто отправил приложение в чат, и администраторы чата. |
| `author` | Только тот, кто отправил приложение в чат. |

## Пределы {#limits}

| Что | Предел |
| --- | --- |
| Значение слота или документа | 16 КиБ |
| Запись журнала | 4 КиБ |
| Записей в журнале | 10 000 |
| Ключей в счётчике | 1 000 |
| Весь экземпляр | 1 МиБ |
| Личные данные (`localStorage`) | 256 КиБ |
| Хранилищ в манифесте | 16 |

- Записей от человека в одном контексте — не больше 10 в секунду.
- И не больше 300 в минуту.

При превышении запись отклоняется с кодом `rate_limited`.
