Хранилище данных плагина
Помимо взаимодействия с публичным API Трекера, плагину доступно собственное JSON-хранилище — storageApi. Оно работает на стороне хоста, поэтому данные не зависят от устройства пользователя и доступны во всех вкладках, где открыт плагин.
Хранилище удобно для пользовательских настроек плагина, кешей справочников и любых данных, которые плагину нужно держать рядом с пользователем, но не сохранять в Трекере как сущность.
Контексты
Каждая запись хранится в контексте — он определяет область видимости данных.
На сегодня доступен единственный контекст:
orgShared— данные общие на всю организацию. Запись видят все пользователи плагина в этой организации. Право на запись отдает хост в полеcanWriteкаждой прочитанной записи.
В будущем список контекстов может расшириться — например, контекст уровня пользователя. API спроектирован так, что добавление нового контекста не сломает существующий код.
Общая запись
Контекст orgShared — это общая запись на всю организацию. Не сохраняйте в ней персональные данные пользователя и помните, что параллельные процессы записи видят изменения друг друга. Для совместной работы используйте версионирование и merge-семантику patch.
Базовое использование
Объект storageApi экспортируется из SDK:
import { storageApi } from '@weavix/sdk-react';
// или
import { storageApi } from '@weavix/sdk-core';
// Чтение
const record = await storageApi.orgShared.get('settings');
// record: { data: { theme: 'dark' }, version: 5, canWrite: true, ... } | null
// Создание записи на пустом бакете
await storageApi.orgShared.patch({
bucket: 'settings',
data: { theme: 'dark', notifications: true },
version: 0,
});
// Обновление без знания текущей версии — SDK сам прочитает ее и повторит запрос при конфликтах
await storageApi.orgShared.patch({
bucket: 'settings',
data: { count: 42 },
});
// Удаление отдельного поля — передайте null
await storageApi.orgShared.patch({
bucket: 'settings',
data: { theme: null },
});
Бакеты
Внутри одного контекста плагин может держать несколько независимых записей — каждая идентифицируется параметром bucket. Это просто строковый ключ. Запись с одним бакетом не пересекается с записью под другим.
await storageApi.orgShared.patch({ bucket: 'settings', data: { theme: 'dark' } });
await storageApi.orgShared.patch({ bucket: 'cache', data: { fetchedAt: Date.now() } });
Если bucket не передан, хост подставит 'default'. Невалидный по формату или длине ключ приведет к ошибке BAD_KEY.
API
storageApi.orgShared.get(bucket?)
Возвращает текущую запись по бакету или null, если записи еще нет.
Параметры:
| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
bucket |
`string | undefined` | хост подставляет 'default' |
Возвращает: Promise<StorageRecord | null>
const record = await storageApi.orgShared.get('settings');
if (record) {
console.log(record.data); // содержимое записи
console.log(record.version); // текущая версия (для оптимистических обновлений)
console.log(record.canWrite); // есть ли у текущего пользователя право писать
} else {
// записи еще нет — ее можно создать через patch с version: 0
}
storageApi.orgShared.patch(options)
Merge-patch: поля из data накладываются на текущую запись поверх, значение null удаляет ключ. Возвращает полный объединенный StorageRecord с новой версией — включая поля, дописанные параллельными процессами записи.
Параметры:
| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
bucket |
string |
хост подставляет 'default' |
Ключ записи |
data |
Record<string, unknown> |
— | Merge-doc; null удаляет поле |
version |
`number | undefined` | auto-resolve |
Возвращает: Promise<StorageRecord> — полная запись после применения патча.
const updated = await storageApi.orgShared.patch({
bucket: 'settings',
data: { theme: 'dark' },
});
updated.version; // версия после патча
updated.data; // полное содержимое записи (включая чужие поля)
Версионирование
Хранилище работает по схеме оптимистических обновлений: у каждой записи есть version, и patch должен передавать ожидаемую версию. Если в хосте уже более свежая версия — операция отклоняется с VERSION_CONFLICT.
Поведение patch зависит от того, передаете ли вы version:
-
С явным
version— отправляется один запрос. Любая ошибка, включаяVERSION_CONFLICT, пробрасывается вызывающему. Используйте этот режим, если приложение само держит актуальную версию и должно реагировать на конфликт — например, показать пользователю «данные изменились, перечитайте». Для создания записи на пустом бакете передавайтеversion: 0. -
Без
version— SDK сначала читает текущую версию черезget(для пустого бакета берется0), затем выполняетpatch. НаVERSION_CONFLICTцикл повторяется до двух раз, каждый повтор заново читает версию. Любая другая ошибка пробрасывается мгновенно. В худшем случае — 6 обращений к хосту (3 пары GET + PATCH). На каждом повторе в консоль пишетсяconsole.warn.
Когда какой режим выбирать
- Передавайте
versionявно, если вы загрузили запись на экран и пользователь ее редактирует — так конфликт с другим писателем не будет потерян молча. - Не передавайте
version, если изменение не зависит от того, что было раньше — например, инкрементируете счетчик или дописываете новое поле.
Право на запись
Поле canWrite в StorageRecord показывает, может ли текущий пользователь делать patch в этой записи. Используйте его, чтобы заранее показать пользователю, что у него нет прав, не дожидаясь ошибки от хоста.
const record = await storageApi.orgShared.get('settings');
if (!record?.canWrite) {
// спрятать или отключить элементы редактирования
}
Коды ошибок
Все ошибки storageApi — экземпляры PluginActionError с числовыми кодами. Константы экспортируются из SDK:
import {
VERSION_CONFLICT,
DATA_TOO_LARGE,
BAD_KEY,
} from '@weavix/sdk-react';
| Константа | Код | Когда |
|---|---|---|
VERSION_CONFLICT |
1010 |
Переданный version не совпал с текущим |
DATA_TOO_LARGE |
1011 |
Размер записи после операции превысил 256 KiB |
BAD_KEY |
1012 |
bucket не прошел валидацию формата или длины |
Общие коды ошибок API (валидация, scope и т.д.) — в разделе Коды ошибок API.
Пример обработки VERSION_CONFLICT:
import {
storageApi,
PluginActionError,
VERSION_CONFLICT,
} from '@weavix/sdk-react';
try {
await storageApi.orgShared.patch({
bucket: 'settings',
data: { theme: 'dark' },
version: knownVersion,
});
} catch (e) {
if (e instanceof PluginActionError && e.code === VERSION_CONFLICT) {
// данные изменились — перечитайте запись и предложите пользователю объединить изменения
const fresh = await storageApi.orgShared.get('settings');
// ...
} else {
throw e;
}
}
Типы
import type {
StorageRecord,
StorageContextType,
StorageGetPayload,
StoragePatchPayload,
} from '@weavix/sdk-react';
StorageRecord
Запись в хранилище.
type StorageRecord = {
/** Внутренний составной ключ записи (контекст + бакет). */
key: Array<{ organization_shared: string }>;
/** Версия записи. Передавайте ее в patch для оптимистических обновлений. */
version: number;
/** Полезные данные записи. */
data: Record<string, unknown>;
/** Может ли текущий пользователь делать patch. */
canWrite: boolean;
/** Дата создания записи (ISO 8601). */
createdAt: string;
/** Дата последнего обновления (ISO 8601). */
updatedAt: string;
};
StorageContextType
type StorageContextType = 'orgShared';
Ограничения
- Максимальный размер записи — 256 KiB после применения операции. Превышение —
DATA_TOO_LARGE. - Ключ бакета валидируется хостом по формату и длине. Невалидный ключ —
BAD_KEY. patchбез явнойversionделает до 2 повторов наVERSION_CONFLICT. Дальше ошибка пробрасывается вызывающему.