UNPKG

synapse-storage

Version:

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

70 lines (69 loc) 5 kB
import { IEventEmitter, ILogger, StorageCapabilities, StorageType, WorkerStorageConfig } from '../storage.interface'; import { StorageKeyType } from '../utils/storage-key'; import { AsyncBaseStorage } from './async-base-storage.service'; /** * Асинхронное хранилище поверх общего key-value стора внутри SharedWorker. * * Это тот же принцип, что у {@link MemoryStorage} (данные в `Map`), но `Map` живёт * ВНУТРИ SharedWorker и потому разделяется между вкладками. Если SharedWorker * недоступен (тесты/SSR/CSP), {@link WorkerChannel} прозрачно откатывается на * in-process `Map` — round-trip идентичен MemoryStorage, но БЕЗ межвкладочного * шеринга (это даёт только настоящий SharedWorker). * * Все `do*` реализованы через RPC к стору ({@link WorkerChannel.request}) и повторяют * маппинг ключей/путей MemoryStorage (те же `getValueByPath`/`setValueByPath`/ * `removeValueByPath`), поэтому поведение совпадает с MemoryStorage «ключ-в-ключ». * * Записи атомарны на уровне ключа (per-key `set`/`update`/`delete`), поэтому * конкурентные вкладки не затирают чужие ключи. После мутации из другой вкладки * стор рассылает уведомление — адаптер обновляет свой кэш и будит подписчиков. */ export declare class WorkerCacheStorage<T extends Record<string, any>> extends AsyncBaseStorage<T> { protected static readonly STORAGE_TYPE: StorageType; readonly type: StorageType; /** Живой кэш; `shared` честно отражает, реально ли работает кросс-табный SharedWorker. */ readonly capabilities: StorageCapabilities; private readonly channelName; private readonly backend; private unsubscribeMutation?; private refreshInFlight; private refreshDirty; constructor(config: WorkerStorageConfig<T>, eventEmitter?: IEventEmitter, logger?: ILogger); static create<T extends Record<string, any>>(config: WorkerStorageConfig<T>, eventEmitter?: IEventEmitter, logger?: ILogger): WorkerCacheStorage<T>; protected doInitialize(): Promise<this>; /** * Подписка на мутации из ДРУГИХ вкладок/экземпляров. Воркер рассылает STORE_MUTATION * после каждой мутирующей операции; payload несёт только `{op}`, поэтому мы просто * перечитываем полное состояние, обновляем `_stateCache` и будим подписчиков (как * при локальной записи). Без этого хука `subscribe()` и tagIndex в QueryStorage * устаревали бы для записей, сделанных другими вкладками. */ private subscribeToStoreMutations; /** * Сериализует refresh'и: пока один в полёте, следующие мутации лишь взводят `dirty`, * и после завершения текущего прогоняется ровно один догоняющий refresh. Так каждый * refresh диффит против актуального `_stateCache`, а не против устаревшего снапшота — * нет дублей уведомлений подписчиков одними и теми же значениями. */ private scheduleRefresh; private runRefreshLoop; private refreshFromRemoteMutation; protected doGet(key: StorageKeyType): Promise<any>; protected doSet(key: StorageKeyType, value: any): Promise<void>; protected doUpdate(updates: Array<{ key: StorageKeyType; value: any; }>): Promise<void>; protected doDelete(key: StorageKeyType): Promise<boolean>; protected doClear(): Promise<void>; protected doKeys(): Promise<string[]>; protected doHas(key: StorageKeyType): Promise<boolean>; /** * Живой кэш НЕ очищается при destroy: данные живут в воркере и разделяются между * вкладками — уничтожение адаптера одной вкладкой не должно стирать общий кэш. * Поэтому переопределяем performCleanup (как IndexedDBStorage), пропуская doClear(), * но ОБЯЗАТЕЛЬНО прогоняя cleanup мидлвар (иначе их каналы/таймеры текут — R8). */ protected performCleanup(): Promise<void>; protected doDestroy(): Promise<void>; }