synapse-storage
Version:
Набор инструментов для управления состоянием и апи-запросами
130 lines (129 loc) • 7.21 kB
TypeScript
import { ApiContext, CacheConfig, RequestDefinition, RetryConfig } from './api.interface';
import { QueryOptions, QueryResult, Unsubscribe } from './query.interface';
/**
* Конфигурация эндпоинта
*/
export interface EndpointConfig<RequestParams extends Record<string, any> = any, RequestResult = any> {
/** Функция для создания определения запроса из параметров */
request: (params: RequestParams, context?: Record<string, any>) => RequestDefinition<RequestParams>;
/** Настройки кэша для эндпоинта */
cache?: CacheConfig;
/** Теги эндпоинта для группировки в кэше */
tags?: string[];
/** Теги, которые инвалидируются при успешном запросе */
invalidatesTags?: string[];
/** Функция для подготовки заголовков (дополняет глобальную) */
prepareHeaders?: (headers: Headers, context: ApiContext<RequestParams>) => Promise<Headers>;
/** Добавить ключи заголовков, влияющие на кэш (Дополняет глобавльные ключи) */
includeCacheableHeaderKeys?: string[];
/** Исключить ключи заголовков, влияющие на кэш (Дополняет глобавльные ключи) */
excludeCacheableHeaderKeys?: string[];
/** Конфигурация retry для этого эндпоинта (переопределяет глобальную) */
retry?: RetryConfig;
}
/**
* Состояние эндпоинта
* Содержит информацию о текущем состоянии запроса и данные
*/
export interface EndpointState {
/** Статус запроса */
status: 'idle' | 'loading' | 'success' | 'error';
/** Ошибка (при неуспешном запросе) */
error?: Error;
/** Количество вызовов */
fetchCounts: number;
/** Метаданные эндпоинта */
meta: Endpoint['meta'];
/** Какие заголовки участвовали в формировании ключа кэша (итоговые) */
cacheableHeaders: string[];
}
/**
* Статус выполнения запроса
*/
export type RequestStatus = 'idle' | 'loading' | 'success' | 'error';
/**
* Состояние самого запроса
*/
export interface RequestState<ResponseData = any, RequestParams extends Record<string, any> = any, E = Error> {
status: RequestStatus;
data?: ResponseData;
error?: E;
headers: Record<string, any> | Headers;
requestParams: RequestParams;
fromCache: boolean;
}
export interface SubscribeOptions {
/** Автоматически отписаться после завершения запроса */
autoUnsubscribe?: boolean;
}
/**
* Дополнительные методы для request
*/
export interface RequestResponseModify<T, P extends Record<string, any> = any> {
id: string;
/**
* Подписка на изменения состояния запроса
*/
subscribe: (listener: (state: RequestState<T, P>) => void, options?: SubscribeOptions) => VoidFunction;
/**
* Ожидание завершения запроса
* @returns Promise с результатом запроса
*/
wait: () => Promise<QueryResult<T, Error>>;
waitWithCallbacks: (handlers: {
idle?: (request: RequestState<T, P>) => void;
loading?: (request: RequestState<T, P>) => void;
success?: (data: T | undefined, request: RequestState<T, P>) => void;
error?: (error: Error | undefined, request: RequestState<T, P>) => void;
}) => Promise<QueryResult<T, Error>>;
/**
* Отменить запрос
*/
abort: VoidFunction;
then<TResult1 = QueryResult<T, Error>, TResult2 = never>(onfulfilled?: ((value: QueryResult<T, Error>) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: Error) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>;
catch<TResult = never>(onrejected?: ((reason: Error) => TResult | PromiseLike<TResult>) | undefined | null): Promise<QueryResult<T, Error> | TResult>;
finally(onfinally?: VoidFunction | undefined | null): Promise<QueryResult<T, Error>>;
}
/**
* Структура эндпоинта
*
* Эндпоинт - это всего лишь определение того6 как будет вызван метод
* Эндпоинт может быть вызван в разных частях приложения с разными параметрами
* По этому нет смысла хранить ответы так как они будут перезаписываться
* метод subscribe больше нужен для мониторинга
* meta - метаинформация по эндпоинту (то как он сконфигурирован)
*/
export interface Endpoint<RequestParams extends Record<string, any> = any, ResponseData = any> {
/** Счетчик вызова конкретного эндпоинта в проекте */
fetchCounts: number;
/** Выполнить запрос с параметрами */
request: (params: RequestParams, options?: QueryOptions) => RequestResponseModify<ResponseData>;
/** Подписаться на изменения состояния эндпоинта (в основном для сбора статистики) */
subscribe: (callback: (state: EndpointState) => void) => Unsubscribe;
/**
* Синхронно прочитать результат из кэша без сетевого запроса (fast-path для SSR).
* Возвращает `undefined`, если кэш недоступен синхронно (async-хранилище,
* заголовки в ключе кэша, отключённый кэш или протухшая запись).
*/
getCachedSync: (params: RequestParams) => QueryResult<ResponseData, Error> | undefined;
/** Подписка на инвалидацию кэша по тегам эндпоинта (для авто-рефетча хуков) */
onCacheInvalidate: (listener: VoidFunction) => Unsubscribe;
/** Сбросить состояние */
reset: () => Promise<void>;
/** Метаданные эндпоинта */
meta: {
/** Имя эндпоинта */
name: string;
/** Теги эндпоинта */
tags: string[];
/** Теги, которые инвалидируются */
invalidatesTags: string[];
/** Настройки кэша */
cache: CacheConfig;
};
destroy: VoidFunction;
}
/**
* Функция для создания типизированных эндпоинтов
*/
export type CreateEndpoint = <RequestParams extends Record<string, any>, RequestResult>(config: EndpointConfig<RequestParams, RequestResult>) => EndpointConfig<RequestParams, RequestResult>;