synapse-storage 4.1.2 → 5.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +142 -12
- package/dist/_utils/chunk.util.d.ts +0 -1
- package/dist/_utils/deepMerge.util.d.ts +0 -1
- package/dist/_utils/error-handling.util.d.ts +0 -1
- package/dist/_utils/flatMap.util.d.ts +0 -1
- package/dist/_utils/index.d.ts +0 -1
- package/dist/_utils/logger-console.util.d.ts +0 -1
- package/dist/api/api.module.d.ts +0 -1
- package/dist/api/components/endpoint.d.ts +11 -12
- package/dist/api/components/endpoint.js.map +1 -1
- package/dist/api/components/query-storage.d.ts +0 -1
- package/dist/api/components/query-storage.js +1 -1
- package/dist/api/components/query-storage.js.map +1 -1
- package/dist/api/index.d.ts +0 -1
- package/dist/api/types/api.interface.d.ts +0 -1
- package/dist/api/types/endpoint.interface.d.ts +0 -1
- package/dist/api/types/query.interface.d.ts +0 -1
- package/dist/api/utils/api-helpers.d.ts +0 -1
- package/dist/{core/storage → api}/utils/cache.util.d.ts +3 -6
- package/dist/{core/storage → api}/utils/cache.util.js +4 -7
- package/dist/api/utils/cache.util.js.map +1 -0
- package/dist/api/utils/create-header-context.d.ts +0 -1
- package/dist/api/utils/endpoint-headers.d.ts +0 -1
- package/dist/api/utils/fetch-base-query.d.ts +0 -1
- package/dist/api/utils/file-utils.d.ts +0 -1
- package/dist/api/utils/get-cacheable-headers.d.ts +0 -1
- package/dist/core/index.d.ts +0 -1
- package/dist/core/selector/index.d.ts +1 -1
- package/dist/core/selector/index.js +2 -0
- package/dist/core/selector/index.js.map +1 -1
- package/dist/core/selector/selector.interface.d.ts +15 -30
- package/dist/core/selector/selector.interface.js +2 -2
- package/dist/core/selector/selector.interface.js.map +1 -1
- package/dist/core/selector/selector.module.d.ts +16 -1
- package/dist/core/selector/selector.module.js +82 -20
- package/dist/core/selector/selector.module.js.map +1 -1
- package/dist/core/selector/selectors.base.d.ts +56 -0
- package/dist/core/selector/selectors.base.js +118 -0
- package/dist/core/selector/selectors.base.js.map +1 -0
- package/dist/core/storage/adapters/async-base-storage.service.d.ts +15 -4
- package/dist/core/storage/adapters/async-base-storage.service.js +106 -36
- package/dist/core/storage/adapters/async-base-storage.service.js.map +1 -1
- package/dist/core/storage/adapters/indexed-DB.service.d.ts +4 -5
- package/dist/core/storage/adapters/indexed-DB.service.js +66 -14
- package/dist/core/storage/adapters/indexed-DB.service.js.map +1 -1
- package/dist/core/storage/adapters/local-storage.service.d.ts +9 -4
- package/dist/core/storage/adapters/local-storage.service.js +25 -5
- package/dist/core/storage/adapters/local-storage.service.js.map +1 -1
- package/dist/core/storage/adapters/memory-storage.service.d.ts +2 -4
- package/dist/core/storage/adapters/memory-storage.service.js +5 -5
- package/dist/core/storage/adapters/memory-storage.service.js.map +1 -1
- package/dist/core/storage/adapters/path.utils.d.ts +0 -1
- package/dist/core/storage/adapters/storage-core.d.ts +6 -2
- package/dist/core/storage/adapters/storage-core.js +6 -3
- package/dist/core/storage/adapters/storage-core.js.map +1 -1
- package/dist/core/storage/adapters/sync-base-storage.service.d.ts +20 -4
- package/dist/core/storage/adapters/sync-base-storage.service.js +110 -35
- package/dist/core/storage/adapters/sync-base-storage.service.js.map +1 -1
- package/dist/core/storage/index.d.ts +2 -5
- package/dist/core/storage/index.js +1 -5
- package/dist/core/storage/index.js.map +1 -1
- package/dist/core/storage/middlewares/broadcast.middleware.d.ts +0 -1
- package/dist/core/storage/middlewares/index.d.ts +3 -1
- package/dist/core/storage/middlewares/index.js +6 -0
- package/dist/core/storage/middlewares/index.js.map +1 -1
- package/dist/core/storage/middlewares/storage-batching.middleware.d.ts +0 -1
- package/dist/core/storage/middlewares/storage-logger.middleware.d.ts +20 -0
- package/dist/core/storage/middlewares/storage-logger.middleware.js +53 -0
- package/dist/core/storage/middlewares/storage-logger.middleware.js.map +1 -0
- package/dist/core/storage/middlewares/storage-shallow-compare.middleware.d.ts +0 -1
- package/dist/core/storage/middlewares/storage-shallow-compare.middleware.js +4 -10
- package/dist/core/storage/middlewares/storage-shallow-compare.middleware.js.map +1 -1
- package/dist/core/storage/middlewares/sync-broadcast.middleware.d.ts +0 -1
- package/dist/core/storage/middlewares/sync-storage-batching.middleware.d.ts +0 -1
- package/dist/core/storage/middlewares/sync-storage-logger.middleware.d.ts +7 -0
- package/dist/core/storage/middlewares/sync-storage-logger.middleware.js +48 -0
- package/dist/core/storage/middlewares/sync-storage-logger.middleware.js.map +1 -0
- package/dist/core/storage/middlewares/sync-storage-shallow-compare.middleware.d.ts +0 -1
- package/dist/core/storage/middlewares/sync-storage-shallow-compare.middleware.js +4 -10
- package/dist/core/storage/middlewares/sync-storage-shallow-compare.middleware.js.map +1 -1
- package/dist/core/storage/modules/singleton/mixin.util.d.ts +0 -1
- package/dist/core/storage/modules/singleton/models.d.ts +0 -1
- package/dist/core/storage/modules/singleton/singleton.util.d.ts +0 -1
- package/dist/core/storage/storage.interface.d.ts +59 -4
- package/dist/core/storage/storage.interface.js +0 -2
- package/dist/core/storage/storage.interface.js.map +1 -1
- package/dist/core/storage/utils/broadcast.util.d.ts +0 -1
- package/dist/core/storage/utils/middleware-module.d.ts +0 -3
- package/dist/core/storage/utils/middleware-module.js +0 -8
- package/dist/core/storage/utils/middleware-module.js.map +1 -1
- package/dist/core/storage/utils/migration.util.d.ts +38 -0
- package/dist/core/storage/utils/migration.util.js +48 -0
- package/dist/core/storage/utils/migration.util.js.map +1 -0
- package/dist/core/storage/utils/path-selector.util.d.ts +0 -1
- package/dist/core/storage/utils/state-diff.util.d.ts +8 -1
- package/dist/core/storage/utils/state-diff.util.js +17 -1
- package/dist/core/storage/utils/state-diff.util.js.map +1 -1
- package/dist/core/storage/utils/storage-factory.util.d.ts +7 -9
- package/dist/core/storage/utils/storage-factory.util.js +10 -10
- package/dist/core/storage/utils/storage-factory.util.js.map +1 -1
- package/dist/core/storage/utils/storage-key.d.ts +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/react/hooks/index.d.ts +2 -1
- package/dist/react/hooks/index.js +4 -0
- package/dist/react/hooks/index.js.map +1 -1
- package/dist/react/hooks/useCreateStorage.d.ts +5 -6
- package/dist/react/hooks/useCreateStorage.js +2 -2
- package/dist/react/hooks/useCreateStorage.js.map +1 -1
- package/dist/react/hooks/useObservable.d.ts +17 -0
- package/dist/react/hooks/useObservable.js +38 -0
- package/dist/react/hooks/useObservable.js.map +1 -0
- package/dist/react/hooks/useSelector.d.ts +0 -1
- package/dist/react/hooks/useSelector.js +5 -2
- package/dist/react/hooks/useSelector.js.map +1 -1
- package/dist/react/hooks/useStorage.d.ts +0 -1
- package/dist/react/hooks/useStorageSubscribe.d.ts +0 -1
- package/dist/react/hooks/useSubscription.d.ts +13 -0
- package/dist/react/hooks/useSubscription.js +23 -0
- package/dist/react/hooks/useSubscription.js.map +1 -0
- package/dist/react/index.d.ts +0 -1
- package/dist/react/utils/awaitSynapse.d.ts +9 -10
- package/dist/react/utils/awaitSynapse.js +3 -2
- package/dist/react/utils/awaitSynapse.js.map +1 -1
- package/dist/react/utils/createSynapseCtx.d.ts +18 -23
- package/dist/react/utils/createSynapseCtx.js +64 -39
- package/dist/react/utils/createSynapseCtx.js.map +1 -1
- package/dist/react/utils/index.d.ts +0 -1
- package/dist/reactive/dispatcher/dispatcher.base.d.ts +122 -0
- package/dist/reactive/dispatcher/dispatcher.base.js +294 -0
- package/dist/reactive/dispatcher/dispatcher.base.js.map +1 -0
- package/dist/reactive/dispatcher/dispatcher.module.d.ts +12 -67
- package/dist/reactive/dispatcher/dispatcher.module.js +13 -72
- package/dist/reactive/dispatcher/dispatcher.module.js.map +1 -1
- package/dist/reactive/dispatcher/index.d.ts +1 -1
- package/dist/reactive/dispatcher/index.js +2 -0
- package/dist/reactive/dispatcher/index.js.map +1 -1
- package/dist/reactive/dispatcher/middlewares/index.d.ts +0 -1
- package/dist/reactive/dispatcher/middlewares/logger.middleware.d.ts +0 -1
- package/dist/reactive/dispatcher/path.util.d.ts +15 -0
- package/dist/reactive/dispatcher/path.util.js +34 -0
- package/dist/reactive/dispatcher/path.util.js.map +1 -0
- package/dist/reactive/dispatcher/standalone.d.ts +1 -150
- package/dist/reactive/dispatcher/standalone.js +6 -217
- package/dist/reactive/dispatcher/standalone.js.map +1 -1
- package/dist/reactive/effects/effects.base.d.ts +62 -0
- package/dist/reactive/effects/effects.base.js +90 -0
- package/dist/reactive/effects/effects.base.js.map +1 -0
- package/dist/reactive/effects/effects.module.d.ts +122 -11
- package/dist/reactive/effects/effects.module.js +129 -17
- package/dist/reactive/effects/effects.module.js.map +1 -1
- package/dist/reactive/effects/index.d.ts +1 -1
- package/dist/reactive/effects/index.js +2 -0
- package/dist/reactive/effects/index.js.map +1 -1
- package/dist/reactive/effects/utils/chunkRequestConsistent.d.ts +0 -1
- package/dist/reactive/effects/utils/chunkRequestParallel.d.ts +0 -1
- package/dist/reactive/effects/utils/fromRequest.d.ts +0 -1
- package/dist/reactive/effects/utils/index.d.ts +0 -1
- package/dist/reactive/effects/utils/toObservable.d.ts +0 -1
- package/dist/reactive/index.d.ts +0 -1
- package/dist/utils/createEventBus.d.ts +47 -46
- package/dist/utils/createEventBus.js +152 -174
- package/dist/utils/createEventBus.js.map +1 -1
- package/dist/utils/createSynapse/createSynapse.d.ts +25 -7
- package/dist/utils/createSynapse/createSynapse.js +28 -98
- package/dist/utils/createSynapse/createSynapse.js.map +1 -1
- package/dist/utils/createSynapse/factory.d.ts +6 -0
- package/dist/utils/createSynapse/factory.js +256 -0
- package/dist/utils/createSynapse/factory.js.map +1 -0
- package/dist/utils/createSynapse/index.d.ts +2 -2
- package/dist/utils/createSynapse/index.js.map +1 -1
- package/dist/utils/createSynapse/synapse.types.d.ts +87 -0
- package/dist/utils/createSynapse/synapse.types.js +11 -0
- package/dist/utils/createSynapse/synapse.types.js.map +1 -0
- package/dist/utils/createSynapse/types.d.ts +6 -85
- package/dist/utils/createSynapse/types.js +2 -1
- package/dist/utils/createSynapse/types.js.map +1 -1
- package/dist/utils/createSynapse/waitForDependencies.d.ts +0 -1
- package/dist/utils/createSynapse/waitForDependencies.js +1 -1
- package/dist/utils/createSynapse/waitForDependencies.js.map +1 -1
- package/dist/utils/createSynapseAwaiter.d.ts +13 -10
- package/dist/utils/createSynapseAwaiter.js +30 -3
- package/dist/utils/createSynapseAwaiter.js.map +1 -1
- package/dist/utils/dehydrateModule.d.ts +6 -0
- package/dist/utils/dehydrateModule.js +43 -0
- package/dist/utils/dehydrateModule.js.map +1 -0
- package/dist/utils/index.d.ts +3 -3
- package/dist/utils/index.js +3 -0
- package/dist/utils/index.js.map +1 -1
- package/package.json +12 -2
- package/dist/_utils/chunk.util.d.ts.map +0 -1
- package/dist/_utils/deepMerge.util.d.ts.map +0 -1
- package/dist/_utils/error-handling.util.d.ts.map +0 -1
- package/dist/_utils/flatMap.util.d.ts.map +0 -1
- package/dist/_utils/index.d.ts.map +0 -1
- package/dist/_utils/logger-console.util.d.ts.map +0 -1
- package/dist/api/api.module.d.ts.map +0 -1
- package/dist/api/components/endpoint.d.ts.map +0 -1
- package/dist/api/components/query-storage.d.ts.map +0 -1
- package/dist/api/example.d.ts +0 -83
- package/dist/api/example.d.ts.map +0 -1
- package/dist/api/example.js +0 -90
- package/dist/api/example.js.map +0 -1
- package/dist/api/index.d.ts.map +0 -1
- package/dist/api/types/api.interface.d.ts.map +0 -1
- package/dist/api/types/endpoint.interface.d.ts.map +0 -1
- package/dist/api/types/query.interface.d.ts.map +0 -1
- package/dist/api/utils/api-helpers.d.ts.map +0 -1
- package/dist/api/utils/create-header-context.d.ts.map +0 -1
- package/dist/api/utils/endpoint-headers.d.ts.map +0 -1
- package/dist/api/utils/fetch-base-query.d.ts.map +0 -1
- package/dist/api/utils/file-utils.d.ts.map +0 -1
- package/dist/api/utils/get-cacheable-headers.d.ts.map +0 -1
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/selector/index.d.ts.map +0 -1
- package/dist/core/selector/selector.interface.d.ts.map +0 -1
- package/dist/core/selector/selector.module.d.ts.map +0 -1
- package/dist/core/storage/adapters/async-base-storage.service.d.ts.map +0 -1
- package/dist/core/storage/adapters/indexed-DB.service.d.ts.map +0 -1
- package/dist/core/storage/adapters/local-storage.service.d.ts.map +0 -1
- package/dist/core/storage/adapters/memory-storage.service.d.ts.map +0 -1
- package/dist/core/storage/adapters/path.utils.d.ts.map +0 -1
- package/dist/core/storage/adapters/storage-core.d.ts.map +0 -1
- package/dist/core/storage/adapters/sync-base-storage.service.d.ts.map +0 -1
- package/dist/core/storage/index.d.ts.map +0 -1
- package/dist/core/storage/middlewares/broadcast.middleware.d.ts.map +0 -1
- package/dist/core/storage/middlewares/index.d.ts.map +0 -1
- package/dist/core/storage/middlewares/storage-batching.middleware.d.ts.map +0 -1
- package/dist/core/storage/middlewares/storage-shallow-compare.middleware.d.ts.map +0 -1
- package/dist/core/storage/middlewares/sync-broadcast.middleware.d.ts.map +0 -1
- package/dist/core/storage/middlewares/sync-storage-batching.middleware.d.ts.map +0 -1
- package/dist/core/storage/middlewares/sync-storage-shallow-compare.middleware.d.ts.map +0 -1
- package/dist/core/storage/modules/plugin/plugin.interface.d.ts +0 -101
- package/dist/core/storage/modules/plugin/plugin.interface.d.ts.map +0 -1
- package/dist/core/storage/modules/plugin/plugin.interface.js +0 -8
- package/dist/core/storage/modules/plugin/plugin.interface.js.map +0 -1
- package/dist/core/storage/modules/plugin/plugin.service.d.ts +0 -49
- package/dist/core/storage/modules/plugin/plugin.service.d.ts.map +0 -1
- package/dist/core/storage/modules/plugin/plugin.service.js +0 -406
- package/dist/core/storage/modules/plugin/plugin.service.js.map +0 -1
- package/dist/core/storage/modules/singleton/mixin.util.d.ts.map +0 -1
- package/dist/core/storage/modules/singleton/models.d.ts.map +0 -1
- package/dist/core/storage/modules/singleton/singleton.util.d.ts.map +0 -1
- package/dist/core/storage/storage.interface.d.ts.map +0 -1
- package/dist/core/storage/utils/broadcast.util.d.ts.map +0 -1
- package/dist/core/storage/utils/cache.util.d.ts.map +0 -1
- package/dist/core/storage/utils/cache.util.js.map +0 -1
- package/dist/core/storage/utils/middleware-module.d.ts.map +0 -1
- package/dist/core/storage/utils/path-selector.util.d.ts.map +0 -1
- package/dist/core/storage/utils/state-diff.util.d.ts.map +0 -1
- package/dist/core/storage/utils/storage-factory.util.d.ts.map +0 -1
- package/dist/core/storage/utils/storage-key.d.ts.map +0 -1
- package/dist/core/storage/utils/storage.utils.d.ts +0 -17
- package/dist/core/storage/utils/storage.utils.d.ts.map +0 -1
- package/dist/core/storage/utils/storage.utils.js +0 -57
- package/dist/core/storage/utils/storage.utils.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/react/hooks/index.d.ts.map +0 -1
- package/dist/react/hooks/useCreateStorage.d.ts.map +0 -1
- package/dist/react/hooks/useSelector.d.ts.map +0 -1
- package/dist/react/hooks/useStorage.d.ts.map +0 -1
- package/dist/react/hooks/useStorageSubscribe.d.ts.map +0 -1
- package/dist/react/index.d.ts.map +0 -1
- package/dist/react/utils/awaitSynapse.d.ts.map +0 -1
- package/dist/react/utils/createSynapseCtx.d.ts.map +0 -1
- package/dist/react/utils/index.d.ts.map +0 -1
- package/dist/reactive/dispatcher/dispatcher.module.d.ts.map +0 -1
- package/dist/reactive/dispatcher/index.d.ts.map +0 -1
- package/dist/reactive/dispatcher/middlewares/index.d.ts.map +0 -1
- package/dist/reactive/dispatcher/middlewares/logger.middleware.d.ts.map +0 -1
- package/dist/reactive/dispatcher/standalone.d.ts.map +0 -1
- package/dist/reactive/effects/effects.module.d.ts.map +0 -1
- package/dist/reactive/effects/index.d.ts.map +0 -1
- package/dist/reactive/effects/utils/chunkRequestConsistent.d.ts.map +0 -1
- package/dist/reactive/effects/utils/chunkRequestParallel.d.ts.map +0 -1
- package/dist/reactive/effects/utils/fromRequest.d.ts.map +0 -1
- package/dist/reactive/effects/utils/index.d.ts.map +0 -1
- package/dist/reactive/effects/utils/toObservable.d.ts.map +0 -1
- package/dist/reactive/index.d.ts.map +0 -1
- package/dist/utils/createEventBus.d.ts.map +0 -1
- package/dist/utils/createSynapse/createSynapse.d.ts.map +0 -1
- package/dist/utils/createSynapse/index.d.ts.map +0 -1
- package/dist/utils/createSynapse/types.d.ts.map +0 -1
- package/dist/utils/createSynapse/validate.d.ts +0 -2
- package/dist/utils/createSynapse/validate.d.ts.map +0 -1
- package/dist/utils/createSynapse/validate.js +0 -76
- package/dist/utils/createSynapse/validate.js.map +0 -1
- package/dist/utils/createSynapse/waitForDependencies.d.ts.map +0 -1
- package/dist/utils/createSynapseAwaiter.d.ts.map +0 -1
- package/dist/utils/index.d.ts.map +0 -1
|
@@ -26,4 +26,3 @@ export declare function buildRequestUrl(path: string, baseUrl: string): URL;
|
|
|
26
26
|
* @returns Функция для выполнения запросов
|
|
27
27
|
*/
|
|
28
28
|
export declare function fetchBaseQuery(options: Omit<FetchBaseQueryArgs, 'prepareHeaders'>): <RequestResult, RequestParams extends Record<string, any>, E extends Error = Error>(args: RequestDefinition<RequestParams>, queryOptions: QueryOptions | undefined, headers: Headers) => Promise<QueryResult<RequestResult, E>>;
|
|
29
|
-
//# sourceMappingURL=fetch-base-query.d.ts.map
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core/selector/index.js","sources":["../../../src/core/selector/index.ts"],"sourcesContent":["export type { ISelectorModule, SelectorAPI } from './selector.interface'\nexport * from './selector.module'\n"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"core/selector/index.js","sources":["../../../src/core/selector/index.ts"],"sourcesContent":["export type { ISelectorModule, SelectorAPI } from './selector.interface'\nexport * from './selector.module'\nexport * from './selectors.base'\n"],"names":[],"mappings":";;AACiC;AACD"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Observable } from 'rxjs';
|
|
1
2
|
export interface Selector<T, R> {
|
|
2
3
|
(state: T): R;
|
|
3
4
|
}
|
|
@@ -13,6 +14,12 @@ export interface SelectorAPI<T> {
|
|
|
13
14
|
selectSync: () => T;
|
|
14
15
|
subscribe: (subscriber: Subscriber<T>) => VoidFunction;
|
|
15
16
|
getId: () => string;
|
|
17
|
+
/**
|
|
18
|
+
* Observable-вид селектора: эмитит текущее значение при подписке и при каждом
|
|
19
|
+
* реальном изменении (та же семантика, что у `subscribe`). Позволяет реактивно
|
|
20
|
+
* трансформировать чтение прямо в компоненте — `selector.$.pipe(debounceTime(300))`.
|
|
21
|
+
*/
|
|
22
|
+
readonly $: Observable<T>;
|
|
16
23
|
/** @internal — проверка готовности источника данных */
|
|
17
24
|
isSourceReady: () => boolean;
|
|
18
25
|
/** @internal — подписка на изменение статуса источника */
|
|
@@ -60,37 +67,15 @@ export interface ISelectorModule<TStore extends Record<string, any>> {
|
|
|
60
67
|
[K in keyof Deps]: SelectorAPI<Deps[K]>;
|
|
61
68
|
}, resultFn: (...args: Deps) => T, options?: SelectorOptions<T>): SelectorAPI<T>;
|
|
62
69
|
/**
|
|
63
|
-
*
|
|
70
|
+
* Точечно удаляет один селектор по его id (`SelectorAPI.getId()`): снимает подписки
|
|
71
|
+
* на хранилище и чистит кэш, не затрагивая остальные селекторы модуля. Нужен для
|
|
72
|
+
* keyed-кэша и для `destroy()` class-селекторов, владеющих общим модулем.
|
|
73
|
+
*
|
|
74
|
+
* @param id Идентификатор селектора (`selector.getId()`)
|
|
64
75
|
*/
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Интерфейс для фабрики селекторов
|
|
69
|
-
* Позволяет создавать набор связанных селекторов
|
|
70
|
-
*/
|
|
71
|
-
export interface ISelectorCreator<TStore extends Record<string, any>, TSelectors, TExternalSelectors = Record<string, any>> {
|
|
76
|
+
removeSelector(id: string): void;
|
|
72
77
|
/**
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* @param selectorModule Модуль селекторов
|
|
76
|
-
* @param externalSelectors Внешние селекторы (опционально)
|
|
77
|
-
* @returns Объект с селекторами
|
|
78
|
-
*
|
|
79
|
-
* @example
|
|
80
|
-
* const createUserSelectors: ISelectorCreator<UserStore, UserSelectors> =
|
|
81
|
-
* (selectorModule, externalSelectors) => {
|
|
82
|
-
* const isActive = selectorModule.createSelector(
|
|
83
|
-
* (state) => state.user.isActive
|
|
84
|
-
* );
|
|
85
|
-
*
|
|
86
|
-
* return { isActive };
|
|
87
|
-
* };
|
|
78
|
+
* Освобождает ресурсы, связанные с модулем селекторов
|
|
88
79
|
*/
|
|
89
|
-
(
|
|
80
|
+
destroy(): void;
|
|
90
81
|
}
|
|
91
|
-
/**
|
|
92
|
-
* Тип для функции, создающей селекторы
|
|
93
|
-
* Более простая версия ISelectorCreator, если не нужен полный интерфейс
|
|
94
|
-
*/
|
|
95
|
-
export type SelectorCreatorFunction<TStore extends Record<string, any> = Record<string, any>, TSelectors = any, TExternalSelectors = Record<string, any>> = (selectorModule: ISelectorModule<TStore>, externalSelectors?: TExternalSelectors) => TSelectors;
|
|
96
|
-
//# sourceMappingURL=selector.interface.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core/selector/selector.interface.js","sources":["../../../src/core/selector/selector.interface.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"core/selector/selector.interface.js","sources":["../../../src/core/selector/selector.interface.ts"],"sourcesContent":["import type { Observable } from 'rxjs'\n\nexport interface Selector<T, R> {\n (state: T): R\n}\n\nexport interface SelectorOptions<T> {\n equals?: (a: T, b: T) => boolean\n name?: string\n}\n\nexport interface Subscriber<T> {\n notify: (value: T) => void | Promise<void>\n}\n\nexport interface SelectorAPI<T> {\n select: () => T\n selectSync: () => T\n subscribe: (subscriber: Subscriber<T>) => VoidFunction\n getId: () => string\n /**\n * Observable-вид селектора: эмитит текущее значение при подписке и при каждом\n * реальном изменении (та же семантика, что у `subscribe`). Позволяет реактивно\n * трансформировать чтение прямо в компоненте — `selector.$.pipe(debounceTime(300))`.\n */\n readonly $: Observable<T>\n /** @internal — проверка готовности источника данных */\n isSourceReady: () => boolean\n /** @internal — подписка на изменение статуса источника */\n onSourceStatusChange: (callback: (isReady: boolean) => void) => VoidFunction\n}\n\n/**\n * Интерфейс для модуля селекторов\n * Определяет контракт для работы с селекторами, который должен реализовывать SelectorModule\n */\nexport interface ISelectorModule<TStore extends Record<string, any>> {\n /**\n * Имя связанного хранилища\n */\n readonly storageName: string\n\n /**\n * Создает простой селектор на основе функции выбора\n *\n * @param selector Функция, извлекающая данные из состояния\n * @param options Опции селектора\n * @returns API селектора\n *\n * @example\n * const isActive = selectorModule.createSelector(\n * (state) => state.user.isActive,\n * { name: 'userIsActive' }\n * );\n */\n createSelector<T>(selector: Selector<TStore, T>, options?: SelectorOptions<T>): SelectorAPI<T>\n\n /**\n * Создает комбинированный селектор на основе других селекторов\n *\n * @param dependencies Массив селекторов, от которых зависит новый селектор\n * @param resultFn Функция, комбинирующая результаты зависимостей\n * @param options Опции селектора\n * @returns API селектора\n *\n * @example\n * const userWithStatus = selectorModule.createSelector(\n * [userSelector, statusSelector],\n * (user, status) => ({ ...user, status }),\n * { name: 'userWithStatus' }\n * );\n */\n createSelector<Deps extends unknown[], T>(dependencies: { [K in keyof Deps]: SelectorAPI<Deps[K]> }, resultFn: (...args: Deps) => T, options?: SelectorOptions<T>): SelectorAPI<T>\n\n /**\n * Точечно удаляет один селектор по его id (`SelectorAPI.getId()`): снимает подписки\n * на хранилище и чистит кэш, не затрагивая остальные селекторы модуля. Нужен для\n * keyed-кэша и для `destroy()` class-селекторов, владеющих общим модулем.\n *\n * @param id Идентификатор селектора (`selector.getId()`)\n */\n removeSelector(id: string): void\n\n /**\n * Освобождает ресурсы, связанные с модулем селекторов\n */\n destroy(): void\n}\n"],"names":[],"mappings":"AAgCA;;;CAGC,GAoDA"}
|
|
@@ -14,7 +14,17 @@ export declare class SelectorModule<S extends Record<string, any>> implements IS
|
|
|
14
14
|
private localSelectorCache;
|
|
15
15
|
private batchUpdateInProgress;
|
|
16
16
|
private pendingUpdates;
|
|
17
|
+
private readonly disposeStatusListener;
|
|
17
18
|
constructor(source: IStorage<S>, logger?: ILogger | undefined);
|
|
19
|
+
/**
|
|
20
|
+
* Собирает публичный `SelectorAPI` поверх подписки. `$` — Observable-вид с той же
|
|
21
|
+
* семантикой, что у `subscribe`: синхронный снапшот при подписке + emit на изменение.
|
|
22
|
+
*
|
|
23
|
+
* `sourceReadiness` переопределяет дефолтную (локальную) готовность источника: для
|
|
24
|
+
* combined-селекторов туда передаётся агрегированная готовность по всем источникам
|
|
25
|
+
* зависимостей (см. {@link createCombinedSelector}).
|
|
26
|
+
*/
|
|
27
|
+
private buildSelectorApi;
|
|
18
28
|
private isSourceReady;
|
|
19
29
|
private onSourceStatusChange;
|
|
20
30
|
/**
|
|
@@ -31,6 +41,11 @@ export declare class SelectorModule<S extends Record<string, any>> implements IS
|
|
|
31
41
|
}, resultFn: (...args: Deps) => T, options?: SelectorOptions<T>): SelectorAPI<T>;
|
|
32
42
|
private createSimpleSelector;
|
|
33
43
|
private createCombinedSelector;
|
|
44
|
+
/**
|
|
45
|
+
* Точечно удаляет селектор по id: снимает его подписки на хранилище, чистит подписку
|
|
46
|
+
* и кэш. Остальные селекторы модуля не затрагиваются. Имя кэша совпадает с id подписки
|
|
47
|
+
* (`generateName()`/`options.name`), поэтому достаточно одного ключа.
|
|
48
|
+
*/
|
|
49
|
+
removeSelector(id: string): void;
|
|
34
50
|
destroy(): void;
|
|
35
51
|
}
|
|
36
|
-
//# sourceMappingURL=selector.module.d.ts.map
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { Observable } from "rxjs";
|
|
1
2
|
import { StorageStatus } from "../storage/index.js";
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
|
|
6
|
+
|
|
7
|
+
|
|
5
8
|
/**
|
|
6
9
|
* Reference equality (===) — поведение по умолчанию
|
|
7
10
|
*/ function defaultEquals(a, b) {
|
|
@@ -174,10 +177,54 @@ class SelectorModule {
|
|
|
174
177
|
// Флаг для батчинга обновлений
|
|
175
178
|
batchUpdateInProgress = false;
|
|
176
179
|
pendingUpdates = new Set();
|
|
180
|
+
// Снятие подписки на статус источника (см. конструктор) — вызывается в destroy().
|
|
181
|
+
disposeStatusListener;
|
|
177
182
|
constructor(source, logger){
|
|
178
183
|
this.source = source;
|
|
179
184
|
this.logger = logger;
|
|
180
185
|
this.storageName = source.name;
|
|
186
|
+
// Селекторы конструируются ДО storage.initialize() (так устроен пайплайн createSynapse:
|
|
187
|
+
// фабрика создаёт селекторы, и только потом buildSynapse зовёт initialize()). На этот
|
|
188
|
+
// момент getStateSync() === {}. combine-селектор при создании эагерно подписывается на
|
|
189
|
+
// зависимости и СИНХРОННО вычисляет их (SelectorSubscription.subscribe при !hasValue),
|
|
190
|
+
// т.е. кеширует значение от пустого стора. А performInitialize наполняет _stateCache
|
|
191
|
+
// молча, без storage:update, — значит этот кеш сам по себе уже не обновится.
|
|
192
|
+
// Поэтому на переходе источника в READY принудительно пересчитываем все подписки.
|
|
193
|
+
// Порядок Map = порядок создания, а зависимость всегда создаётся раньше зависимого
|
|
194
|
+
// (combine([this.api]) невозможен до this.api) → пересчёт идёт «снизу вверх» корректно.
|
|
195
|
+
this.disposeStatusListener = this.source.onStatusChange((status)=>{
|
|
196
|
+
if (status.status === StorageStatus.READY) {
|
|
197
|
+
this.subscriptions.forEach((sub)=>{
|
|
198
|
+
try {
|
|
199
|
+
sub.notify();
|
|
200
|
+
} catch (error) {
|
|
201
|
+
this.logger?.error(`[${sub.getId()}] Ошибка пересчёта на READY`, {
|
|
202
|
+
error
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Собирает публичный `SelectorAPI` поверх подписки. `$` — Observable-вид с той же
|
|
211
|
+
* семантикой, что у `subscribe`: синхронный снапшот при подписке + emit на изменение.
|
|
212
|
+
*
|
|
213
|
+
* `sourceReadiness` переопределяет дефолтную (локальную) готовность источника: для
|
|
214
|
+
* combined-селекторов туда передаётся агрегированная готовность по всем источникам
|
|
215
|
+
* зависимостей (см. {@link createCombinedSelector}).
|
|
216
|
+
*/ buildSelectorApi(id, subscription, getState, sourceReadiness) {
|
|
217
|
+
return {
|
|
218
|
+
select: ()=>getState(),
|
|
219
|
+
selectSync: ()=>subscription.getValue(),
|
|
220
|
+
subscribe: (subscriber)=>subscription.subscribe(subscriber),
|
|
221
|
+
getId: ()=>id,
|
|
222
|
+
$: new Observable((observer)=>subscription.subscribe({
|
|
223
|
+
notify: (value)=>observer.next(value)
|
|
224
|
+
})),
|
|
225
|
+
isSourceReady: sourceReadiness?.isSourceReady ?? this.isSourceReady,
|
|
226
|
+
onSourceStatusChange: sourceReadiness?.onSourceStatusChange ?? this.onSourceStatusChange
|
|
227
|
+
};
|
|
181
228
|
}
|
|
182
229
|
isSourceReady = ()=>{
|
|
183
230
|
return this.source.initStatus.status === StorageStatus.READY;
|
|
@@ -303,16 +350,7 @@ class SelectorModule {
|
|
|
303
350
|
unsubscribeFromStorage
|
|
304
351
|
];
|
|
305
352
|
return {
|
|
306
|
-
api:
|
|
307
|
-
select: ()=>getState(),
|
|
308
|
-
selectSync: ()=>subscription.getValue(),
|
|
309
|
-
subscribe: (subscriber)=>{
|
|
310
|
-
return subscription.subscribe(subscriber);
|
|
311
|
-
},
|
|
312
|
-
getId: ()=>id,
|
|
313
|
-
isSourceReady: this.isSourceReady,
|
|
314
|
-
onSourceStatusChange: this.onSourceStatusChange
|
|
315
|
-
},
|
|
353
|
+
api: this.buildSelectorApi(id, subscription, getState),
|
|
316
354
|
unsubscribeFunctions
|
|
317
355
|
};
|
|
318
356
|
}
|
|
@@ -379,21 +417,45 @@ class SelectorModule {
|
|
|
379
417
|
unsubscribeFunctions.push(()=>{
|
|
380
418
|
destroyed = true;
|
|
381
419
|
});
|
|
420
|
+
// Агрегированная готовность источников: combined-селектор «готов», только когда готов
|
|
421
|
+
// ЛОКАЛЬНЫЙ источник И все источники зависимостей (важно для cross-store combine, где
|
|
422
|
+
// зависимости приходят из других сторов с собственным lifecycle).
|
|
423
|
+
const aggregatedSourceReadiness = {
|
|
424
|
+
isSourceReady: ()=>this.isSourceReady() && selectors.every((dep)=>dep.isSourceReady()),
|
|
425
|
+
onSourceStatusChange: (callback)=>{
|
|
426
|
+
const emit = ()=>callback(aggregatedSourceReadiness.isSourceReady());
|
|
427
|
+
const disposers = [
|
|
428
|
+
this.onSourceStatusChange(emit),
|
|
429
|
+
...selectors.map((dep)=>dep.onSourceStatusChange(emit))
|
|
430
|
+
];
|
|
431
|
+
return ()=>disposers.forEach((dispose)=>dispose());
|
|
432
|
+
}
|
|
433
|
+
};
|
|
382
434
|
return {
|
|
383
|
-
api:
|
|
384
|
-
select: ()=>getState(),
|
|
385
|
-
selectSync: ()=>subscription.getValue(),
|
|
386
|
-
subscribe: (subscriber)=>{
|
|
387
|
-
return subscription.subscribe(subscriber);
|
|
388
|
-
},
|
|
389
|
-
getId: ()=>id,
|
|
390
|
-
isSourceReady: this.isSourceReady,
|
|
391
|
-
onSourceStatusChange: this.onSourceStatusChange
|
|
392
|
-
},
|
|
435
|
+
api: this.buildSelectorApi(id, subscription, getState, aggregatedSourceReadiness),
|
|
393
436
|
unsubscribeFunctions
|
|
394
437
|
};
|
|
395
438
|
}
|
|
439
|
+
/**
|
|
440
|
+
* Точечно удаляет селектор по id: снимает его подписки на хранилище, чистит подписку
|
|
441
|
+
* и кэш. Остальные селекторы модуля не затрагиваются. Имя кэша совпадает с id подписки
|
|
442
|
+
* (`generateName()`/`options.name`), поэтому достаточно одного ключа.
|
|
443
|
+
*/ removeSelector(id) {
|
|
444
|
+
const subscription = this.subscriptions.get(id);
|
|
445
|
+
if (subscription) {
|
|
446
|
+
subscription.cleanup();
|
|
447
|
+
this.subscriptions.delete(id);
|
|
448
|
+
}
|
|
449
|
+
this.pendingUpdates.delete(id);
|
|
450
|
+
const cached = this.localSelectorCache.get(id);
|
|
451
|
+
if (cached) {
|
|
452
|
+
cached.unsubscribeFunctions.forEach((unsub)=>unsub());
|
|
453
|
+
this.localSelectorCache.delete(id);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
396
456
|
destroy() {
|
|
457
|
+
// Снимаем подписку на статус источника
|
|
458
|
+
this.disposeStatusListener();
|
|
397
459
|
// Очищаем все подписки
|
|
398
460
|
this.subscriptions.forEach((sub)=>sub.cleanup());
|
|
399
461
|
this.subscriptions.clear();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core/selector/selector.module.js","sources":["../../../src/core/selector/selector.module.ts"],"sourcesContent":["import type { ILogger, IStorage, StorageInitStatus } from '../storage'\nimport { StorageStatus } from '../storage'\nimport type { ISelectorModule, Selector, SelectorAPI, SelectorOptions, Subscriber } from './selector.interface'\n\n/**\n * Reference equality (===) — поведение по умолчанию\n */\nfunction defaultEquals<T>(a: T, b: T): boolean {\n return a === b\n}\n\nconst MAX_DEEP_EQUAL_DEPTH = 10\n\n/**\n * Глубокое сравнение объектов по структуре с защитой от циклических ссылок и лимитом глубины.\n * Используется только при явной передаче через options.equals.\n */\nexport function deepEquals<T>(a: T, b: T, depth = 0, visited = new WeakSet()): boolean {\n if (a === b) return true\n if (a == null || b == null) return false\n if (typeof a !== typeof b) return false\n if (depth > MAX_DEEP_EQUAL_DEPTH) return false\n\n if (typeof a !== 'object') return a === b\n\n // Защита от циклических ссылок\n if (visited.has(a as object)) return false\n visited.add(a as object)\n\n if (a instanceof Date && b instanceof Date) {\n return a.getTime() === b.getTime()\n }\n\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false\n for (let i = 0; i < a.length; i++) {\n if (!deepEquals(a[i], b[i], depth + 1, visited)) return false\n }\n return true\n }\n\n if (Array.isArray(a) || Array.isArray(b)) return false\n\n const keysA = Object.keys(a as object)\n const keysB = Object.keys(b as object)\n\n if (keysA.length !== keysB.length) return false\n\n return keysA.every((key) => {\n if (!Object.prototype.hasOwnProperty.call(b, key)) return false\n return deepEquals((a as any)[key], (b as any)[key], depth + 1, visited)\n })\n}\n\n/**\n * Оборачивает state в Proxy для отслеживания обращений к ключам верхнего уровня.\n * Возвращает проксированный state и Set с именами ключей, к которым обратился селектор.\n */\nfunction trackDependencies<S extends Record<string, any>>(state: S): { proxy: S; accessedKeys: Set<string> } {\n const accessedKeys = new Set<string>()\n\n const proxy = new Proxy(state, {\n get(target, prop, receiver) {\n if (typeof prop === 'string') {\n accessedKeys.add(prop)\n }\n return Reflect.get(target, prop, receiver)\n },\n })\n\n return { proxy, accessedKeys }\n}\n\n// Мемоизирует функцию селектора для оптимизации + трекинг зависимостей через Proxy\nfunction memoizeSelector<S extends Record<string, any>, R>(\n selectorFn: (state: S) => R,\n equals: (a: R, b: R) => boolean = defaultEquals,\n): { memoized: (state: S) => R; getTrackedKeys: () => Set<string> | null } {\n let lastState: S | undefined\n let lastResult: R | undefined\n let hasResult = false\n let trackedKeys: Set<string> | null = null\n\n const memoized = function (state: S): R {\n // Если это первый вызов или состояние изменилось\n if (!hasResult || lastState !== state) {\n // Трекаем зависимости через Proxy при каждом реальном пересчёте\n const { proxy, accessedKeys } = trackDependencies(state)\n const newResult = selectorFn(proxy)\n trackedKeys = accessedKeys\n\n // Проверяем, изменился ли результат\n if (!hasResult || !equals(newResult, lastResult as R)) {\n lastResult = newResult\n }\n\n lastState = state\n hasResult = true\n }\n\n return lastResult as R\n }\n\n return { memoized, getTrackedKeys: () => trackedKeys }\n}\n\nclass SelectorSubscription<T> {\n private readonly id: string\n readonly subscribers = new Set<Subscriber<T>>()\n private lastValue?: T\n private hasValue = false\n\n constructor(\n private readonly name: string,\n private readonly getState: () => T,\n private readonly equals: (a: T, b: T) => boolean = defaultEquals,\n private readonly logger?: ILogger,\n ) {\n this.id = name\n this.logger?.debug(`[${this.id}] SelectorSubscription created`)\n }\n\n notify(): void {\n try {\n const newValue = this.getState()\n\n // Проверка на изменение значения с использованием функции сравнения\n if (!this.hasValue || !this.equals(newValue, this.lastValue as T)) {\n this.logger?.debug(`[${this.id}] Value changed, notify()`)\n\n this.lastValue = newValue\n this.hasValue = true\n\n for (const subscriber of this.subscribers) {\n try {\n subscriber.notify(newValue)\n } catch (error) {\n this.logger?.error(`[${this.id}] Ошибка в уведомлении подписчика`, { error })\n }\n }\n }\n } catch (error: any) {\n this.logger?.error(`[${this.id}] Ошибка в notify()`, { error })\n throw error\n }\n }\n\n subscribe(subscriber: Subscriber<T>): VoidFunction {\n this.subscribers.add(subscriber)\n\n // Отправляем текущее значение синхронно\n if (this.hasValue) {\n try {\n subscriber.notify(this.lastValue as T)\n } catch (error) {\n this.logger?.error(`[${this.id}] Ошибка в первоначальном уведомлении`, { error })\n }\n } else {\n // Вычисляем значение синхронно\n this.notify()\n }\n\n return () => {\n this.subscribers.delete(subscriber)\n }\n }\n\n cleanup(): void {\n this.subscribers.clear()\n this.lastValue = undefined\n this.hasValue = false\n }\n\n getLastValue(): T | undefined {\n return this.lastValue\n }\n\n getValue(): T {\n if (!this.hasValue) {\n this.lastValue = this.getState()\n this.hasValue = true\n }\n return this.lastValue as T\n }\n\n getId(): string {\n return this.id\n }\n}\n\nexport class SelectorModule<S extends Record<string, any>> implements ISelectorModule<S> {\n storageName: string\n\n private selectorIdCounter = 0\n private subscriptions = new Map<string, SelectorSubscription<any>>()\n\n private localSelectorCache = new Map<\n string,\n {\n api: SelectorAPI<any>\n dependencies?: SelectorAPI<any>[]\n unsubscribeFunctions: Array<() => void>\n }\n >()\n\n // Флаг для батчинга обновлений\n private batchUpdateInProgress = false\n private pendingUpdates = new Set<string>()\n\n constructor(\n private readonly source: IStorage<S>,\n private readonly logger?: ILogger,\n ) {\n this.storageName = source.name\n }\n\n private isSourceReady = (): boolean => {\n return this.source.initStatus.status === StorageStatus.READY\n }\n\n private onSourceStatusChange = (callback: (isReady: boolean) => void): VoidFunction => {\n return this.source.onStatusChange((status: StorageInitStatus) => {\n callback(status.status === StorageStatus.READY)\n })\n }\n\n /**\n * Генерирует уникальное имя для селектора через автоинкрементный счётчик\n */\n private generateName(): string {\n return `${this.storageName}_selector_${this.selectorIdCounter++}`\n }\n\n /**\n * Обрабатывает отложенные обновления синхронно\n */\n private processPendingUpdates(): void {\n if (this.pendingUpdates.size === 0 || this.batchUpdateInProgress) return\n\n this.batchUpdateInProgress = true\n\n try {\n // Копируем список селекторов для обновления\n const subscriptionsToUpdate = Array.from(this.pendingUpdates)\n this.pendingUpdates.clear()\n\n // Обновляем все ожидающие селекторы синхронно\n for (const id of subscriptionsToUpdate) {\n const subscription = this.subscriptions.get(id)\n if (subscription) {\n try {\n subscription.notify()\n } catch (error) {\n this.logger?.error(`Ошибка уведомления подписчика ${id}`, { error })\n }\n }\n }\n } catch (error) {\n this.logger?.error('Ошибка обработки ожидающих обновлений', { error })\n } finally {\n this.batchUpdateInProgress = false\n\n // Если появились новые обновления во время обработки, запускаем процесс снова\n if (this.pendingUpdates.size > 0) {\n this.processPendingUpdates()\n }\n }\n }\n\n createSelector<T>(selector: Selector<S, T>, options?: SelectorOptions<T>): SelectorAPI<T>\n createSelector<Deps extends unknown[], T>(dependencies: { [K in keyof Deps]: SelectorAPI<Deps[K]> }, resultFn: (...args: Deps) => T, options?: SelectorOptions<T>): SelectorAPI<T>\n\n createSelector<T>(\n selectorOrDeps: Selector<S, T> | SelectorAPI<any>[],\n resultFnOrOptions?: ((...args: any[]) => T) | SelectorOptions<T>,\n optionsArg?: SelectorOptions<T>,\n ): SelectorAPI<T> {\n // Определяем, какую перегрузку используем\n const isSimpleSelector = !Array.isArray(selectorOrDeps)\n\n // Извлекаем options\n const options = isSimpleSelector ? (resultFnOrOptions as SelectorOptions<T>) || {} : optionsArg || {}\n\n const hasExplicitName = !!options.name\n\n // Используем предоставленное имя или генерируем уникальный ID\n const selectorId = options.name || this.generateName()\n\n // Кеширование по имени — только для явно именованных селекторов\n if (hasExplicitName && this.localSelectorCache.has(selectorId)) {\n return this.localSelectorCache.get(selectorId)!.api\n }\n\n // Создаем новый селектор\n let result: SelectorAPI<T>\n let dependencies: SelectorAPI<any>[] | undefined\n let unsubscribeFunctions: VoidFunction[] = []\n\n if (isSimpleSelector) {\n // Простой селектор с мемоизацией и трекингом зависимостей\n const { memoized, getTrackedKeys } = memoizeSelector(selectorOrDeps as Selector<S, T>, options.equals || defaultEquals)\n\n const created = this.createSimpleSelector(memoized, getTrackedKeys, { ...options, name: selectorId, equals: options.equals || defaultEquals })\n\n result = created.api\n unsubscribeFunctions = created.unsubscribeFunctions\n } else {\n // Комбинированный селектор\n dependencies = selectorOrDeps as SelectorAPI<any>[]\n\n const created = this.createCombinedSelector(dependencies, resultFnOrOptions as (...args: any[]) => T, {\n ...options,\n name: selectorId,\n equals: options.equals || defaultEquals,\n })\n\n result = created.api\n unsubscribeFunctions = created.unsubscribeFunctions\n }\n\n // Сохраняем в кеши\n this.localSelectorCache.set(selectorId, {\n api: result,\n dependencies,\n unsubscribeFunctions,\n })\n\n return result\n }\n\n private createSimpleSelector<T>(\n selector: Selector<S, T>,\n getTrackedKeys: () => Set<string> | null,\n options: SelectorOptions<T> & { name: string },\n ): {\n api: SelectorAPI<T>\n unsubscribeFunctions: VoidFunction[]\n } {\n // Синхронное получение состояния из кеша\n const getState = (): T => {\n const state = this.source.getStateSync()\n return selector(state as S)\n }\n\n const subscription = new SelectorSubscription(options.name, getState, options.equals || defaultEquals, this.logger)\n\n const id = subscription.getId()\n this.subscriptions.set(id, subscription)\n\n // Подписка на обновления хранилища с фильтрацией по changedPaths\n const unsubscribeFromStorage = this.source.subscribeToAll((event: any) => {\n if (event?.type === 'storage:update') {\n // Фильтруем по changedPaths: если зависимости известны и нет пересечения — пропускаем\n const changedPaths: string[] | undefined = event?.changedPaths\n const deps = getTrackedKeys()\n\n if (changedPaths && deps && deps.size > 0) {\n const hasRelevantChange = changedPaths.some((path) => {\n // changedPaths может содержать вложенные пути (\"users.0.name\")\n // берём ключ верхнего уровня для сравнения с deps\n const topLevelKey = path.split('.')[0]\n return deps.has(topLevelKey)\n })\n if (!hasRelevantChange) return\n }\n\n this.pendingUpdates.add(id)\n this.processPendingUpdates()\n }\n })\n\n const unsubscribeFunctions = [unsubscribeFromStorage]\n\n return {\n api: {\n select: () => getState(),\n selectSync: () => subscription.getValue(),\n subscribe: (subscriber) => {\n return subscription.subscribe(subscriber)\n },\n getId: () => id,\n isSourceReady: this.isSourceReady,\n onSourceStatusChange: this.onSourceStatusChange,\n },\n unsubscribeFunctions,\n }\n }\n\n private createCombinedSelector<Deps extends unknown[], T>(\n selectors: { [K in keyof Deps]: SelectorAPI<Deps[K]> },\n resultFn: (...args: Deps) => T,\n options: SelectorOptions<T> & { name: string },\n ): {\n api: SelectorAPI<T>\n unsubscribeFunctions: Array<VoidFunction>\n } {\n // Мемоизация для combined selectors: поэлементное сравнение аргументов по ссылке (как reselect)\n let lastArgs: Deps | undefined\n let lastResult: T | undefined\n let hasResult = false\n const equals = options.equals || defaultEquals\n\n const memoizedResultFn = (args: Deps): T => {\n // Проверяем, изменился ли хотя бы один аргумент\n if (hasResult && lastArgs && lastArgs.length === args.length) {\n let argsEqual = true\n for (let i = 0; i < args.length; i++) {\n if (lastArgs[i] !== args[i]) {\n argsEqual = false\n break\n }\n }\n if (argsEqual) return lastResult as T\n }\n\n const newResult = resultFn(...args)\n\n // Сохраняем аргументы без создания нового массива — переиспользуем переданный\n lastArgs = args\n\n if (!hasResult || !equals(newResult, lastResult as T)) {\n lastResult = newResult\n }\n\n hasResult = true\n return lastResult as T\n }\n\n const getState = (): T => {\n const values = selectors.map((s) => s.selectSync()) as Deps\n return memoizedResultFn(values)\n }\n\n const subscription = new SelectorSubscription(options.name, getState, options.equals || defaultEquals, this.logger)\n\n const id = subscription.getId()\n this.subscriptions.set(id, subscription)\n\n // Батчинг обновлений через microtask — без искусственной задержки\n let pendingNotify = false\n let destroyed = false\n\n const triggerUpdate = () => {\n if (!pendingNotify) {\n pendingNotify = true\n queueMicrotask(() => {\n pendingNotify = false\n if (!destroyed) {\n try {\n subscription.notify()\n } catch (error) {\n this.logger?.error(`[${id}] Ошибка в объединенном уведомлении:`, { error })\n }\n }\n })\n }\n }\n\n const unsubscribeFunctions: Array<VoidFunction> = selectors.map((selector) =>\n selector.subscribe({\n notify: () => {\n triggerUpdate()\n },\n }),\n )\n\n // При уничтожении предотвращаем выполнение отложенного microtask\n unsubscribeFunctions.push(() => {\n destroyed = true\n })\n\n return {\n api: {\n select: () => getState(),\n selectSync: () => subscription.getValue(),\n subscribe: (subscriber) => {\n return subscription.subscribe(subscriber)\n },\n getId: () => id,\n isSourceReady: this.isSourceReady,\n onSourceStatusChange: this.onSourceStatusChange,\n },\n unsubscribeFunctions,\n }\n }\n\n destroy(): void {\n // Очищаем все подписки\n this.subscriptions.forEach((sub) => sub.cleanup())\n this.subscriptions.clear()\n\n // Очищаем список ожидающих обновлений\n this.pendingUpdates.clear()\n\n // Очищаем подписки из локального кеша\n this.localSelectorCache.forEach((cached) => {\n cached.unsubscribeFunctions.forEach((unsub) => unsub())\n })\n this.localSelectorCache.clear()\n }\n}\n"],"names":["StorageStatus","defaultEquals","a","b","MAX_DEEP_EQUAL_DEPTH","deepEquals","depth","visited","WeakSet","Date","Array","i","keysA","Object","keysB","key","trackDependencies","state","accessedKeys","Set","proxy","Proxy","target","prop","receiver","Reflect","memoizeSelector","selectorFn","equals","lastState","lastResult","hasResult","trackedKeys","memoized","newResult","SelectorSubscription","name","getState","logger","newValue","subscriber","error","undefined","SelectorModule","Map","source","callback","status","subscriptionsToUpdate","id","subscription","selectorOrDeps","resultFnOrOptions","optionsArg","isSimpleSelector","options","hasExplicitName","selectorId","result","dependencies","unsubscribeFunctions","getTrackedKeys","created","selector","unsubscribeFromStorage","event","changedPaths","deps","hasRelevantChange","path","topLevelKey","selectors","resultFn","lastArgs","memoizedResultFn","args","argsEqual","values","s","pendingNotify","destroyed","triggerUpdate","queueMicrotask","sub","cached","unsub"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"core/selector/selector.module.js","sources":["../../../src/core/selector/selector.module.ts"],"sourcesContent":["import { Observable } from 'rxjs'\n\nimport type { ILogger, IStorage, StorageInitStatus } from '../storage'\nimport { StorageStatus } from '../storage'\nimport type { ISelectorModule, Selector, SelectorAPI, SelectorOptions, Subscriber } from './selector.interface'\n\n/**\n * Reference equality (===) — поведение по умолчанию\n */\nfunction defaultEquals<T>(a: T, b: T): boolean {\n return a === b\n}\n\nconst MAX_DEEP_EQUAL_DEPTH = 10\n\n/**\n * Глубокое сравнение объектов по структуре с защитой от циклических ссылок и лимитом глубины.\n * Используется только при явной передаче через options.equals.\n */\nexport function deepEquals<T>(a: T, b: T, depth = 0, visited = new WeakSet()): boolean {\n if (a === b) return true\n if (a == null || b == null) return false\n if (typeof a !== typeof b) return false\n if (depth > MAX_DEEP_EQUAL_DEPTH) return false\n\n if (typeof a !== 'object') return a === b\n\n // Защита от циклических ссылок\n if (visited.has(a as object)) return false\n visited.add(a as object)\n\n if (a instanceof Date && b instanceof Date) {\n return a.getTime() === b.getTime()\n }\n\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false\n for (let i = 0; i < a.length; i++) {\n if (!deepEquals(a[i], b[i], depth + 1, visited)) return false\n }\n return true\n }\n\n if (Array.isArray(a) || Array.isArray(b)) return false\n\n const keysA = Object.keys(a as object)\n const keysB = Object.keys(b as object)\n\n if (keysA.length !== keysB.length) return false\n\n return keysA.every((key) => {\n if (!Object.prototype.hasOwnProperty.call(b, key)) return false\n return deepEquals((a as any)[key], (b as any)[key], depth + 1, visited)\n })\n}\n\n/**\n * Оборачивает state в Proxy для отслеживания обращений к ключам верхнего уровня.\n * Возвращает проксированный state и Set с именами ключей, к которым обратился селектор.\n */\nfunction trackDependencies<S extends Record<string, any>>(state: S): { proxy: S; accessedKeys: Set<string> } {\n const accessedKeys = new Set<string>()\n\n const proxy = new Proxy(state, {\n get(target, prop, receiver) {\n if (typeof prop === 'string') {\n accessedKeys.add(prop)\n }\n return Reflect.get(target, prop, receiver)\n },\n })\n\n return { proxy, accessedKeys }\n}\n\n// Мемоизирует функцию селектора для оптимизации + трекинг зависимостей через Proxy\nfunction memoizeSelector<S extends Record<string, any>, R>(\n selectorFn: (state: S) => R,\n equals: (a: R, b: R) => boolean = defaultEquals,\n): { memoized: (state: S) => R; getTrackedKeys: () => Set<string> | null } {\n let lastState: S | undefined\n let lastResult: R | undefined\n let hasResult = false\n let trackedKeys: Set<string> | null = null\n\n const memoized = function (state: S): R {\n // Если это первый вызов или состояние изменилось\n if (!hasResult || lastState !== state) {\n // Трекаем зависимости через Proxy при каждом реальном пересчёте\n const { proxy, accessedKeys } = trackDependencies(state)\n const newResult = selectorFn(proxy)\n trackedKeys = accessedKeys\n\n // Проверяем, изменился ли результат\n if (!hasResult || !equals(newResult, lastResult as R)) {\n lastResult = newResult\n }\n\n lastState = state\n hasResult = true\n }\n\n return lastResult as R\n }\n\n return { memoized, getTrackedKeys: () => trackedKeys }\n}\n\nclass SelectorSubscription<T> {\n private readonly id: string\n readonly subscribers = new Set<Subscriber<T>>()\n private lastValue?: T\n private hasValue = false\n\n constructor(\n private readonly name: string,\n private readonly getState: () => T,\n private readonly equals: (a: T, b: T) => boolean = defaultEquals,\n private readonly logger?: ILogger,\n ) {\n this.id = name\n this.logger?.debug(`[${this.id}] SelectorSubscription created`)\n }\n\n notify(): void {\n try {\n const newValue = this.getState()\n\n // Проверка на изменение значения с использованием функции сравнения\n if (!this.hasValue || !this.equals(newValue, this.lastValue as T)) {\n this.logger?.debug(`[${this.id}] Value changed, notify()`)\n\n this.lastValue = newValue\n this.hasValue = true\n\n for (const subscriber of this.subscribers) {\n try {\n subscriber.notify(newValue)\n } catch (error) {\n this.logger?.error(`[${this.id}] Ошибка в уведомлении подписчика`, { error })\n }\n }\n }\n } catch (error: any) {\n this.logger?.error(`[${this.id}] Ошибка в notify()`, { error })\n throw error\n }\n }\n\n subscribe(subscriber: Subscriber<T>): VoidFunction {\n this.subscribers.add(subscriber)\n\n // Отправляем текущее значение синхронно\n if (this.hasValue) {\n try {\n subscriber.notify(this.lastValue as T)\n } catch (error) {\n this.logger?.error(`[${this.id}] Ошибка в первоначальном уведомлении`, { error })\n }\n } else {\n // Вычисляем значение синхронно\n this.notify()\n }\n\n return () => {\n this.subscribers.delete(subscriber)\n }\n }\n\n cleanup(): void {\n this.subscribers.clear()\n this.lastValue = undefined\n this.hasValue = false\n }\n\n getLastValue(): T | undefined {\n return this.lastValue\n }\n\n getValue(): T {\n if (!this.hasValue) {\n this.lastValue = this.getState()\n this.hasValue = true\n }\n return this.lastValue as T\n }\n\n getId(): string {\n return this.id\n }\n}\n\nexport class SelectorModule<S extends Record<string, any>> implements ISelectorModule<S> {\n storageName: string\n\n private selectorIdCounter = 0\n private subscriptions = new Map<string, SelectorSubscription<any>>()\n\n private localSelectorCache = new Map<\n string,\n {\n api: SelectorAPI<any>\n dependencies?: SelectorAPI<any>[]\n unsubscribeFunctions: Array<() => void>\n }\n >()\n\n // Флаг для батчинга обновлений\n private batchUpdateInProgress = false\n private pendingUpdates = new Set<string>()\n\n // Снятие подписки на статус источника (см. конструктор) — вызывается в destroy().\n private readonly disposeStatusListener: VoidFunction\n\n constructor(\n private readonly source: IStorage<S>,\n private readonly logger?: ILogger,\n ) {\n this.storageName = source.name\n\n // Селекторы конструируются ДО storage.initialize() (так устроен пайплайн createSynapse:\n // фабрика создаёт селекторы, и только потом buildSynapse зовёт initialize()). На этот\n // момент getStateSync() === {}. combine-селектор при создании эагерно подписывается на\n // зависимости и СИНХРОННО вычисляет их (SelectorSubscription.subscribe при !hasValue),\n // т.е. кеширует значение от пустого стора. А performInitialize наполняет _stateCache\n // молча, без storage:update, — значит этот кеш сам по себе уже не обновится.\n // Поэтому на переходе источника в READY принудительно пересчитываем все подписки.\n // Порядок Map = порядок создания, а зависимость всегда создаётся раньше зависимого\n // (combine([this.api]) невозможен до this.api) → пересчёт идёт «снизу вверх» корректно.\n this.disposeStatusListener = this.source.onStatusChange((status) => {\n if (status.status === StorageStatus.READY) {\n this.subscriptions.forEach((sub) => {\n try {\n sub.notify()\n } catch (error) {\n this.logger?.error(`[${sub.getId()}] Ошибка пересчёта на READY`, { error })\n }\n })\n }\n })\n }\n\n /**\n * Собирает публичный `SelectorAPI` поверх подписки. `$` — Observable-вид с той же\n * семантикой, что у `subscribe`: синхронный снапшот при подписке + emit на изменение.\n *\n * `sourceReadiness` переопределяет дефолтную (локальную) готовность источника: для\n * combined-селекторов туда передаётся агрегированная готовность по всем источникам\n * зависимостей (см. {@link createCombinedSelector}).\n */\n private buildSelectorApi<T>(\n id: string,\n subscription: SelectorSubscription<T>,\n getState: () => T,\n sourceReadiness?: { isSourceReady: () => boolean; onSourceStatusChange: (callback: (isReady: boolean) => void) => VoidFunction },\n ): SelectorAPI<T> {\n return {\n select: () => getState(),\n selectSync: () => subscription.getValue(),\n subscribe: (subscriber) => subscription.subscribe(subscriber),\n getId: () => id,\n $: new Observable<T>((observer) => subscription.subscribe({ notify: (value) => observer.next(value) })),\n isSourceReady: sourceReadiness?.isSourceReady ?? this.isSourceReady,\n onSourceStatusChange: sourceReadiness?.onSourceStatusChange ?? this.onSourceStatusChange,\n }\n }\n\n private isSourceReady = (): boolean => {\n return this.source.initStatus.status === StorageStatus.READY\n }\n\n private onSourceStatusChange = (callback: (isReady: boolean) => void): VoidFunction => {\n return this.source.onStatusChange((status: StorageInitStatus) => {\n callback(status.status === StorageStatus.READY)\n })\n }\n\n /**\n * Генерирует уникальное имя для селектора через автоинкрементный счётчик\n */\n private generateName(): string {\n return `${this.storageName}_selector_${this.selectorIdCounter++}`\n }\n\n /**\n * Обрабатывает отложенные обновления синхронно\n */\n private processPendingUpdates(): void {\n if (this.pendingUpdates.size === 0 || this.batchUpdateInProgress) return\n\n this.batchUpdateInProgress = true\n\n try {\n // Копируем список селекторов для обновления\n const subscriptionsToUpdate = Array.from(this.pendingUpdates)\n this.pendingUpdates.clear()\n\n // Обновляем все ожидающие селекторы синхронно\n for (const id of subscriptionsToUpdate) {\n const subscription = this.subscriptions.get(id)\n if (subscription) {\n try {\n subscription.notify()\n } catch (error) {\n this.logger?.error(`Ошибка уведомления подписчика ${id}`, { error })\n }\n }\n }\n } catch (error) {\n this.logger?.error('Ошибка обработки ожидающих обновлений', { error })\n } finally {\n this.batchUpdateInProgress = false\n\n // Если появились новые обновления во время обработки, запускаем процесс снова\n if (this.pendingUpdates.size > 0) {\n this.processPendingUpdates()\n }\n }\n }\n\n createSelector<T>(selector: Selector<S, T>, options?: SelectorOptions<T>): SelectorAPI<T>\n createSelector<Deps extends unknown[], T>(dependencies: { [K in keyof Deps]: SelectorAPI<Deps[K]> }, resultFn: (...args: Deps) => T, options?: SelectorOptions<T>): SelectorAPI<T>\n\n createSelector<T>(\n selectorOrDeps: Selector<S, T> | SelectorAPI<any>[],\n resultFnOrOptions?: ((...args: any[]) => T) | SelectorOptions<T>,\n optionsArg?: SelectorOptions<T>,\n ): SelectorAPI<T> {\n // Определяем, какую перегрузку используем\n const isSimpleSelector = !Array.isArray(selectorOrDeps)\n\n // Извлекаем options\n const options = isSimpleSelector ? (resultFnOrOptions as SelectorOptions<T>) || {} : optionsArg || {}\n\n const hasExplicitName = !!options.name\n\n // Используем предоставленное имя или генерируем уникальный ID\n const selectorId = options.name || this.generateName()\n\n // Кеширование по имени — только для явно именованных селекторов\n if (hasExplicitName && this.localSelectorCache.has(selectorId)) {\n return this.localSelectorCache.get(selectorId)!.api\n }\n\n // Создаем новый селектор\n let result: SelectorAPI<T>\n let dependencies: SelectorAPI<any>[] | undefined\n let unsubscribeFunctions: VoidFunction[] = []\n\n if (isSimpleSelector) {\n // Простой селектор с мемоизацией и трекингом зависимостей\n const { memoized, getTrackedKeys } = memoizeSelector(selectorOrDeps as Selector<S, T>, options.equals || defaultEquals)\n\n const created = this.createSimpleSelector(memoized, getTrackedKeys, { ...options, name: selectorId, equals: options.equals || defaultEquals })\n\n result = created.api\n unsubscribeFunctions = created.unsubscribeFunctions\n } else {\n // Комбинированный селектор\n dependencies = selectorOrDeps as SelectorAPI<any>[]\n\n const created = this.createCombinedSelector(dependencies, resultFnOrOptions as (...args: any[]) => T, {\n ...options,\n name: selectorId,\n equals: options.equals || defaultEquals,\n })\n\n result = created.api\n unsubscribeFunctions = created.unsubscribeFunctions\n }\n\n // Сохраняем в кеши\n this.localSelectorCache.set(selectorId, {\n api: result,\n dependencies,\n unsubscribeFunctions,\n })\n\n return result\n }\n\n private createSimpleSelector<T>(\n selector: Selector<S, T>,\n getTrackedKeys: () => Set<string> | null,\n options: SelectorOptions<T> & { name: string },\n ): {\n api: SelectorAPI<T>\n unsubscribeFunctions: VoidFunction[]\n } {\n // Синхронное получение состояния из кеша\n const getState = (): T => {\n const state = this.source.getStateSync()\n return selector(state as S)\n }\n\n const subscription = new SelectorSubscription(options.name, getState, options.equals || defaultEquals, this.logger)\n\n const id = subscription.getId()\n this.subscriptions.set(id, subscription)\n\n // Подписка на обновления хранилища с фильтрацией по changedPaths\n const unsubscribeFromStorage = this.source.subscribeToAll((event: any) => {\n if (event?.type === 'storage:update') {\n // Фильтруем по changedPaths: если зависимости известны и нет пересечения — пропускаем\n const changedPaths: string[] | undefined = event?.changedPaths\n const deps = getTrackedKeys()\n\n if (changedPaths && deps && deps.size > 0) {\n const hasRelevantChange = changedPaths.some((path) => {\n // changedPaths может содержать вложенные пути (\"users.0.name\")\n // берём ключ верхнего уровня для сравнения с deps\n const topLevelKey = path.split('.')[0]\n return deps.has(topLevelKey)\n })\n if (!hasRelevantChange) return\n }\n\n this.pendingUpdates.add(id)\n this.processPendingUpdates()\n }\n })\n\n const unsubscribeFunctions = [unsubscribeFromStorage]\n\n return {\n api: this.buildSelectorApi(id, subscription, getState),\n unsubscribeFunctions,\n }\n }\n\n private createCombinedSelector<Deps extends unknown[], T>(\n selectors: { [K in keyof Deps]: SelectorAPI<Deps[K]> },\n resultFn: (...args: Deps) => T,\n options: SelectorOptions<T> & { name: string },\n ): {\n api: SelectorAPI<T>\n unsubscribeFunctions: Array<VoidFunction>\n } {\n // Мемоизация для combined selectors: поэлементное сравнение аргументов по ссылке (как reselect)\n let lastArgs: Deps | undefined\n let lastResult: T | undefined\n let hasResult = false\n const equals = options.equals || defaultEquals\n\n const memoizedResultFn = (args: Deps): T => {\n // Проверяем, изменился ли хотя бы один аргумент\n if (hasResult && lastArgs && lastArgs.length === args.length) {\n let argsEqual = true\n for (let i = 0; i < args.length; i++) {\n if (lastArgs[i] !== args[i]) {\n argsEqual = false\n break\n }\n }\n if (argsEqual) return lastResult as T\n }\n\n const newResult = resultFn(...args)\n\n // Сохраняем аргументы без создания нового массива — переиспользуем переданный\n lastArgs = args\n\n if (!hasResult || !equals(newResult, lastResult as T)) {\n lastResult = newResult\n }\n\n hasResult = true\n return lastResult as T\n }\n\n const getState = (): T => {\n const values = selectors.map((s) => s.selectSync()) as Deps\n return memoizedResultFn(values)\n }\n\n const subscription = new SelectorSubscription(options.name, getState, options.equals || defaultEquals, this.logger)\n\n const id = subscription.getId()\n this.subscriptions.set(id, subscription)\n\n // Батчинг обновлений через microtask — без искусственной задержки\n let pendingNotify = false\n let destroyed = false\n\n const triggerUpdate = () => {\n if (!pendingNotify) {\n pendingNotify = true\n queueMicrotask(() => {\n pendingNotify = false\n if (!destroyed) {\n try {\n subscription.notify()\n } catch (error) {\n this.logger?.error(`[${id}] Ошибка в объединенном уведомлении:`, { error })\n }\n }\n })\n }\n }\n\n const unsubscribeFunctions: Array<VoidFunction> = selectors.map((selector) =>\n selector.subscribe({\n notify: () => {\n triggerUpdate()\n },\n }),\n )\n\n // При уничтожении предотвращаем выполнение отложенного microtask\n unsubscribeFunctions.push(() => {\n destroyed = true\n })\n\n // Агрегированная готовность источников: combined-селектор «готов», только когда готов\n // ЛОКАЛЬНЫЙ источник И все источники зависимостей (важно для cross-store combine, где\n // зависимости приходят из других сторов с собственным lifecycle).\n const aggregatedSourceReadiness = {\n isSourceReady: (): boolean => this.isSourceReady() && selectors.every((dep) => dep.isSourceReady()),\n onSourceStatusChange: (callback: (isReady: boolean) => void): VoidFunction => {\n const emit = () => callback(aggregatedSourceReadiness.isSourceReady())\n const disposers = [this.onSourceStatusChange(emit), ...selectors.map((dep) => dep.onSourceStatusChange(emit))]\n return () => disposers.forEach((dispose) => dispose())\n },\n }\n\n return {\n api: this.buildSelectorApi(id, subscription, getState, aggregatedSourceReadiness),\n unsubscribeFunctions,\n }\n }\n\n /**\n * Точечно удаляет селектор по id: снимает его подписки на хранилище, чистит подписку\n * и кэш. Остальные селекторы модуля не затрагиваются. Имя кэша совпадает с id подписки\n * (`generateName()`/`options.name`), поэтому достаточно одного ключа.\n */\n removeSelector(id: string): void {\n const subscription = this.subscriptions.get(id)\n if (subscription) {\n subscription.cleanup()\n this.subscriptions.delete(id)\n }\n\n this.pendingUpdates.delete(id)\n\n const cached = this.localSelectorCache.get(id)\n if (cached) {\n cached.unsubscribeFunctions.forEach((unsub) => unsub())\n this.localSelectorCache.delete(id)\n }\n }\n\n destroy(): void {\n // Снимаем подписку на статус источника\n this.disposeStatusListener()\n\n // Очищаем все подписки\n this.subscriptions.forEach((sub) => sub.cleanup())\n this.subscriptions.clear()\n\n // Очищаем список ожидающих обновлений\n this.pendingUpdates.clear()\n\n // Очищаем подписки из локального кеша\n this.localSelectorCache.forEach((cached) => {\n cached.unsubscribeFunctions.forEach((unsub) => unsub())\n })\n this.localSelectorCache.clear()\n }\n}\n"],"names":["Observable","StorageStatus","defaultEquals","a","b","MAX_DEEP_EQUAL_DEPTH","deepEquals","depth","visited","WeakSet","Date","Array","i","keysA","Object","keysB","key","trackDependencies","state","accessedKeys","Set","proxy","Proxy","target","prop","receiver","Reflect","memoizeSelector","selectorFn","equals","lastState","lastResult","hasResult","trackedKeys","memoized","newResult","SelectorSubscription","name","getState","logger","newValue","subscriber","error","undefined","SelectorModule","Map","source","status","sub","id","subscription","sourceReadiness","observer","value","callback","subscriptionsToUpdate","selectorOrDeps","resultFnOrOptions","optionsArg","isSimpleSelector","options","hasExplicitName","selectorId","result","dependencies","unsubscribeFunctions","getTrackedKeys","created","selector","unsubscribeFromStorage","event","changedPaths","deps","hasRelevantChange","path","topLevelKey","selectors","resultFn","lastArgs","memoizedResultFn","args","argsEqual","values","s","pendingNotify","destroyed","triggerUpdate","queueMicrotask","aggregatedSourceReadiness","dep","emit","disposers","dispose","cached","unsub"],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { IStorage } from '../storage';
|
|
2
|
+
import type { ISelectorModule, SelectorAPI, SelectorOptions } from './selector.interface';
|
|
3
|
+
/**
|
|
4
|
+
* Публичный class-based слой селекторов. Селекторы объявляются как поля класса через
|
|
5
|
+
* фабрики `this.select` / `this.combine` / `this.keyed` — поля сразу настоящие
|
|
6
|
+
* `SelectorAPI` (eager-материализация, никаких рецептов).
|
|
7
|
+
*
|
|
8
|
+
* Внешние селекторы (cross-store) передаются параметрами конструктора подкласса:
|
|
9
|
+
* parameter properties присваиваются ДО инициализаторов полей, поэтому `this.core`
|
|
10
|
+
* в полях доступен корректно.
|
|
11
|
+
*
|
|
12
|
+
* Внутреннее состояние базы — hard-private (`#`-поля/методы): их имена в отдельном
|
|
13
|
+
* namespace и НЕ конфликтуют с полями-селекторами подкласса (можно объявить селектор
|
|
14
|
+
* `track`, `module` и т.п.). Зарезервированы лишь `protected`/публичные члены
|
|
15
|
+
* (`select`/`combine`/`keyed`/`destroy`) — их именами селекторы называть нельзя.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* class PostsSelectors extends Selectors<PostsState> {
|
|
20
|
+
* constructor(storage: IStorage<PostsState>, private readonly core: CoreSelectors) {
|
|
21
|
+
* super(storage)
|
|
22
|
+
* }
|
|
23
|
+
*
|
|
24
|
+
* private readonly api = this.select((s) => s.api)
|
|
25
|
+
* readonly list = this.select((s) => s.list)
|
|
26
|
+
* readonly isPostsLoading = this.combine([this.api], (a) => a.postsRequest.status === 'loading')
|
|
27
|
+
* // cross-store: пересчитывается при изменении чужого стора
|
|
28
|
+
* readonly currentUserId = this.combine([this.core.profile], (p) => p?.user_info?.id ?? null)
|
|
29
|
+
* }
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare abstract class Selectors<TState extends Record<string, any>> {
|
|
33
|
+
#private;
|
|
34
|
+
/** Принимает `storage` (создаёт и владеет своим `SelectorModule`) либо готовый модуль. */
|
|
35
|
+
constructor(source: IStorage<TState> | ISelectorModule<TState>);
|
|
36
|
+
/** Простой селектор: мемоизация по ссылке стейта + трекинг затронутых ключей. */
|
|
37
|
+
protected select<R>(selector: (state: TState) => R, options?: SelectorOptions<R>): SelectorAPI<R>;
|
|
38
|
+
/** Combined-селектор; зависимости — любые `SelectorAPI`, в т.ч. из других сторов. */
|
|
39
|
+
protected combine<Deps extends unknown[], R>(deps: {
|
|
40
|
+
[K in keyof Deps]: SelectorAPI<Deps[K]>;
|
|
41
|
+
}, fn: (...args: Deps) => R, options?: SelectorOptions<R>): SelectorAPI<R>;
|
|
42
|
+
/**
|
|
43
|
+
* Параметрический (keyed) селектор: один `SelectorAPI` на ключ (кэш по ключу).
|
|
44
|
+
*
|
|
45
|
+
* Слайсы соседних ключей живут под общим родителем (`s.byTarget[key]`), а storage при
|
|
46
|
+
* обновлении пере-клонирует всю ветку — поэтому ссылка соседнего ключа не сохраняется.
|
|
47
|
+
* Чтобы обновление ключа A не уведомляло подписчиков ключа B, keyed-селекторы по
|
|
48
|
+
* умолчанию сравнивают значения структурно (`deepEquals`). Опции можно переопределить.
|
|
49
|
+
*/
|
|
50
|
+
protected keyed<K extends string | number, R>(fn: (key: K) => (state: TState) => R, options?: SelectorOptions<R>): (key: K) => SelectorAPI<R>;
|
|
51
|
+
/**
|
|
52
|
+
* Уничтожает свой модуль (только если владеет им). Если модуль передан снаружи —
|
|
53
|
+
* удаляет из него лишь свои селекторы, чужие не трогает.
|
|
54
|
+
*/
|
|
55
|
+
destroy(): void;
|
|
56
|
+
}
|