UNPKG

synapse-storage

Version:

Набор инструментов для управления состоянием и апи-запросами

366 lines (359 loc) 17 kB
import { j as IStorage } from './storage.interface-BA_ktyDz.js'; /** * Тип для функции отписки от событий */ type Unsubscribe = VoidFunction; /** * Опции для выполнения запроса */ interface QueryOptions { /** Отключить кэширование для этого запроса */ disableCache?: boolean; /** Signal для отмены запроса */ signal?: AbortSignal; /** Таймаут в миллисекундах (переопределяет глобальный) */ timeout?: number; /** Дополнительные заголовки */ headers?: Headers; /** Пользовательский контекст */ context?: Record<string, any>; /** Ключи заголовков, влияющие на кэш (для конкретного запроса) */ cacheableHeaderKeys?: string[]; /** Формат ответа (переопределяет формат из RequestDefinition) */ responseFormat?: ResponseFormat; /** Название файла при скачивании (переопределяет fileName из RequestDefinition) */ fileName?: string; /** Тип файла при скачивании (переопределяет fileType из RequestDefinition) */ fileType?: string; /** Автоматически скачать файл после получения */ downloadFile?: boolean; /** * Функция для повторного выполнения запроса */ retry?: <T = any, R = any>(params: T, options?: QueryOptions) => Promise<R>; } /** * Метаданные для файла */ interface FileMetadata { /** Имя файла */ fileName: string; /** Тип файла (MIME-тип) */ fileType: string; /** Размер файла в байтах */ size?: number; /** Дата создания файла */ createdAt?: Date | string; /** Дата изменения файла */ updatedAt?: Date | string; } /** * Результат скачивания файла */ interface FileDownloadResult<T = Blob | ArrayBuffer> { /** Данные файла */ data: T; /** Метаданные файла */ metadata: FileMetadata; /** HTTP-статус */ status: number; /** Текст статуса */ statusText: string; /** Заголовки ответа */ headers: Headers; /** Успешна ли загрузка */ ok: boolean; } /** * Результат выполнения запроса */ interface QueryResult<T = any, E = Error> { /** Данные ответа (при успешном запросе) */ data?: T; /** Ошибка (при неуспешном запросе) */ error?: E; /** Флаг успешности запроса */ ok: boolean; /** HTTP-статус */ status: number; /** Текстовое описание статуса */ statusText: string; /** Заголовки ответа */ headers: Headers; /** Результат скачивания файла (если responseFormat - Blob или ArrayBuffer) */ fileDownloadResult?: FileDownloadResult; fromCache?: boolean; } /** * Конфигурация эндпоинта */ 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[]; } /** * Состояние эндпоинта * Содержит информацию о текущем состоянии запроса и данные */ interface EndpointState { /** Статус запроса */ status: 'idle' | 'loading' | 'success' | 'error'; /** Ошибка (при неуспешном запросе) */ error?: Error; /** Количество вызовов */ fetchCounts: number; /** Метаданные эндпоинта */ meta: Endpoint['meta']; /** Какие заголовки участвовали в формировании ключа кэша (итоговые) */ cacheableHeaders: string[]; } /** * Состояние самого запроса */ interface RequestState<ResponseData = any, RequestParams extends Record<string, any> = any, E = Error> { status: 'loading' | 'success' | 'error' | 'idle'; data?: ResponseData; error?: E; headers: Record<string, any> | Headers; requestParams: RequestParams; fromCache: boolean; } interface SubscribeOptions { /** Автоматически отписаться после завершения запроса */ autoUnsubscribe?: boolean; } /** * Дополнительные методы для request */ 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 - метаинформация по эндпоинту (то как он сконфигурирован) */ 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; /** Сбросить состояние */ reset: () => Promise<void>; /** Метаданные эндпоинта */ meta: { /** Имя эндпоинта */ name: string; /** Теги эндпоинта */ tags: string[]; /** Теги, которые инвалидируются */ invalidatesTags: string[]; /** Настройки кэша */ cache: CacheConfig; }; destroy: VoidFunction; } /** * Функция для создания типизированных эндпоинтов */ type CreateEndpoint = <RequestParams extends Record<string, any>, RequestResult>(config: EndpointConfig<RequestParams, RequestResult>) => EndpointConfig<RequestParams, RequestResult>; /** * Форматы ответа от сервера */ declare enum ResponseFormat { /** JSON-объект (по умолчанию) */ Json = "json", /** Blob-объект для файлов */ Blob = "blob", /** ArrayBuffer для бинарных данных */ ArrayBuffer = "arrayBuffer", /** Текстовый формат */ Text = "text", /** FormData для форм */ FormData = "formData", /** Без преобразования - возвращает сырой ответ */ Raw = "raw" } /** * Настройки кэша * Может быть объектом с параметрами или boolean (true для кэширования с настройками по умолчанию, false для отключения) */ type CacheConfig = boolean | { /** Время жизни кэша в миллисекундах */ ttl?: number; /** Настройки периодической очистки */ cleanup?: { /** Включить периодическую очистку */ enabled: boolean; /** Интервал очистки в миллисекундах */ interval?: number; }; /** Инвалидировать кэш при ошибке */ invalidateOnError?: boolean; }; /** * Определение запроса * Содержит всю необходимую информацию для выполнения HTTP-запроса */ interface RequestDefinition<RequestParams extends Record<string, any>> { /** Путь запроса (относительный или абсолютный URL) */ path: string; /** HTTP-метод */ method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'; /** Тело запроса (автоматически сериализуется) */ body?: any; /** Параметры запроса (автоматически добавляются в URL) */ query?: RequestParams; /** HTTP-заголовки */ headers?: Record<string, string>; /** Формат ответа (по умолчанию json) */ responseFormat?: ResponseFormat; /** Имя файла для автоматического скачивания */ fileName?: string; /** Тип контента для автоматического скачивания */ fileType?: string; } /** * Контекст API для использования в prepareHeaders и других функциях * Содержит вспомогательные методы и информацию о запросе */ interface ApiContext<RequestParams extends Record<string, any> = any> { /** Параметры запроса */ requestParams?: RequestParams; /** Получить значение из localStorage */ getFromStorage: <T>(key: string) => T | undefined; /** Получить значение cookie */ getCookie: (name: string) => string | undefined; /** Поддержка для дополнительных свойств */ [key: string]: any; } /** * Аргументы для создания fetch-запроса */ interface FetchBaseQueryArgs { /** Базовый URL для всех запросов */ baseUrl: string; /** Функция для подготовки заголовков, может быть асинхронной */ prepareHeaders?: (headers: Headers, context: ApiContext) => Promise<Headers>; /** Таймаут запроса в миллисекундах */ timeout?: number; /** Пользовательская fetch-функция */ fetchFn?: typeof fetch; credentials?: RequestCredentials; } interface CreateApiClientOptions<T extends Record<string, EndpointConfig<any, any>> = Record<string, EndpointConfig<any, any>>> { storage: IStorage; /** Настройки кэша * если явно указан false - значит ни один запрос НЕ будет кэшироваться, даже если в эндпоинтах указаны параметры * */ cache?: CacheConfig; /** Базовый запрос или его настройки */ baseQuery: FetchBaseQueryArgs; /** Функция для создания эндпоинтов */ endpoints?: (create: CreateEndpoint) => Promise<T>; /** Глобальные заголовки, влияющие на кэш */ cacheableHeaderKeys?: string[]; } /** * Извлечение типа параметров из конфигурации эндпоинта */ type ExtractParamsType<T> = T extends EndpointConfig<infer P, any> ? P : never; /** * Извлечение типа результата из конфигурации эндпоинта */ type ExtractResultType<T> = T extends EndpointConfig<any, infer R> ? R : never; type EndpointsResult<F> = F extends (create: any) => Promise<infer R> ? R : never; declare class ApiClient<EndpointsFn extends (create: CreateEndpoint) => Promise<Record<string, EndpointConfig<any, any>>>> { /** Хранилище запросов */ private queryStorage; private readonly cacheableHeaderKeys; private readonly globalCacheConfig; private readonly baseQueryConfig; private readonly storageExternal; private readonly createEndpoints; /** Реестр эндпоинтов */ private endpoints; constructor(options: Omit<CreateApiClientOptions, 'endpoints'> & { endpoints: EndpointsFn; }); init(): Promise<this>; private initializeEndpoints; /** * Получает все эндпоинты с улучшенной типизацией * @returns Типизированный объект эндпоинтов */ getEndpoints(): { [K in keyof EndpointsResult<EndpointsFn>]: Endpoint<ExtractParamsType<EndpointsResult<EndpointsFn>[K]>, ExtractResultType<EndpointsResult<EndpointsFn>[K]>>; }; /** * Выполняет запрос к API с типизацией и обработкой ошибок * @param endpointName Имя эндпоинта (с подсказками TypeScript) * @param params Параметры запроса (с типизацией) * @param options Опции запроса * @returns Promise с типизированным результатом запроса */ request<K extends keyof EndpointsResult<EndpointsFn> & string>(endpointName: K, params: ExtractParamsType<EndpointsResult<EndpointsFn>[K]>, options?: QueryOptions): Promise<QueryResult<ExtractResultType<EndpointsResult<EndpointsFn>[K]>, Error>>; destroy(): Promise<void>; } /** * Логгер для API */ declare const apiLogger: { debug: (message: string, ...args: any[]) => void; log: (message: string, ...args: any[]) => void; info: (message: string, ...args: any[]) => void; warn: (message: string, ...args: any[]) => void; error: (message: string, ...args: any[]) => void; }; /** * Создает уникальный идентификатор * @returns Строка с уникальным идентификатором */ declare function createUniqueId(name?: string): string; /** * Преобразует объект Headers в обычный объект * @param headers Объект Headers * @returns Обычный объект с заголовками */ declare function headersToObject(headers: Headers): Record<string, string>; export { ApiClient, ResponseFormat, apiLogger, createUniqueId, headersToObject };